import json import logging from functools import partial from django.contrib.auth.models import User from django.test import TestCase from django.test.client import RequestFactory from django.conf import settings from django.core.urlresolvers import reverse from foldit.views import foldit_ops, verify_code from foldit.models import PuzzleComplete, Score from student.models import UserProfile, unique_id_for_user from datetime import datetime, timedelta log = logging.getLogger(__name__) class FolditTestCase(TestCase): def setUp(self): self.factory = RequestFactory() self.url = reverse('foldit_ops') pwd = 'abc' self.user = User.objects.create_user('testuser', 'test@test.com', pwd) self.user2 = User.objects.create_user('testuser2', 'test2@test.com', pwd) self.unique_user_id = unique_id_for_user(self.user) self.unique_user_id2 = unique_id_for_user(self.user2) now = datetime.now() self.tomorrow = now + timedelta(days=1) self.yesterday = now - timedelta(days=1) UserProfile.objects.create(user=self.user) UserProfile.objects.create(user=self.user2) def make_request(self, post_data, user=None): request = self.factory.post(self.url, post_data) request.user = self.user if not user else user return request def make_puzzle_score_request(self, puzzle_ids, best_scores, user=None): """ Given lists of puzzle_ids and best_scores (must have same length), make a SetPlayerPuzzleScores request and return the response. """ if not(type(best_scores) == list): best_scores = [best_scores] if not(type(puzzle_ids) == list): puzzle_ids = [puzzle_ids] user = self.user if not user else user def score_dict(puzzle_id, best_score): return {"PuzzleID": puzzle_id, "ScoreType": "score", "BestScore": best_score, # current scores don't actually matter "CurrentScore": best_score + 0.01, "ScoreVersion": 23} scores = [score_dict(pid, bs) for pid, bs in zip(puzzle_ids, best_scores)] scores_str = json.dumps(scores) verify = {"Verify": verify_code(user.email, scores_str), "VerifyMethod": "FoldItVerify"} data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify), 'SetPlayerPuzzleScores': scores_str} request = self.make_request(data, user) response = foldit_ops(request) self.assertEqual(response.status_code, 200) return response def test_SetPlayerPuzzleScores(self): puzzle_id = 994391 best_score = 0.078034 response = self.make_puzzle_score_request(puzzle_id, [best_score]) self.assertEqual(response.content, json.dumps( [{"OperationID": "SetPlayerPuzzleScores", "Value": [{ "PuzzleID": puzzle_id, "Status": "Success"}]}])) # There should now be a score in the db. top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(best_score)) def test_SetPlayerPuzzleScores_many(self): response = self.make_puzzle_score_request([1, 2], [0.078034, 0.080000]) self.assertEqual(response.content, json.dumps( [{"OperationID": "SetPlayerPuzzleScores", "Value": [{ "PuzzleID": 1, "Status": "Success"}, {"PuzzleID": 2, "Status": "Success"}]}])) def test_SetPlayerPuzzleScores_multiple(self): """ Check that multiple posts with the same id are handled properly (keep latest for each user, have multiple users work properly) """ orig_score = 0.07 puzzle_id = '1' response = self.make_puzzle_score_request([puzzle_id], [orig_score]) # There should now be a score in the db. top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(orig_score)) # Reporting a better score should overwrite better_score = 0.06 response = self.make_puzzle_score_request([1], [better_score]) top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 1) # Floats always get in the way, so do almostequal self.assertAlmostEqual(top_10[0]['score'], Score.display_score(better_score), delta=0.5) # reporting a worse score shouldn't worse_score = 0.065 response = self.make_puzzle_score_request([1], [worse_score]) top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 1) # should still be the better score self.assertAlmostEqual(top_10[0]['score'], Score.display_score(better_score), delta=0.5) def test_SetPlayerPuzzleScores_manyplayers(self): """ Check that when we send scores from multiple users, the correct order of scores is displayed. Note that, before being processed by display_score, lower scores are better. """ puzzle_id = ['1'] player1_score = 0.08 player2_score = 0.02 response1 = self.make_puzzle_score_request(puzzle_id, player1_score, self.user) # There should now be a score in the db. top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 1) self.assertEqual(top_10[0]['score'], Score.display_score(player1_score)) response2 = self.make_puzzle_score_request(puzzle_id, player2_score, self.user2) # There should now be two scores in the db top_10 = Score.get_tops_n(10, puzzle_id) self.assertEqual(len(top_10), 2) # Top score should be player2_score. Second should be player1_score self.assertAlmostEqual(top_10[0]['score'], Score.display_score(player2_score), delta=0.5) self.assertAlmostEqual(top_10[1]['score'], Score.display_score(player1_score), delta=0.5) # Top score user should be self.user2.username self.assertEqual(top_10[0]['username'], self.user2.username) def test_SetPlayerPuzzleScores_error(self): scores = [{"PuzzleID": 994391, "ScoreType": "score", "BestScore": 0.078034, "CurrentScore": 0.080035, "ScoreVersion": 23}] validation_str = json.dumps(scores) verify = {"Verify": verify_code(self.user.email, validation_str), "VerifyMethod": "FoldItVerify"} # change the real string -- should get an error scores[0]['ScoreVersion'] = 22 scores_str = json.dumps(scores) data = {'SetPlayerPuzzleScoresVerify': json.dumps(verify), 'SetPlayerPuzzleScores': scores_str} request = self.make_request(data) response = foldit_ops(request) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) self.assertEqual(response.content, json.dumps([{ "OperationID": "SetPlayerPuzzleScores", "Success": "false", "ErrorString": "Verification failed", "ErrorCode": "VerifyFailed"}])) def make_puzzles_complete_request(self, puzzles): """ Make a puzzles complete request, given an array of puzzles. E.g. [ {"PuzzleID": 13, "Set": 1, "SubSet": 2}, {"PuzzleID": 53524, "Set": 1, "SubSet": 1} ] """ puzzles_str = json.dumps(puzzles) verify = {"Verify": verify_code(self.user.email, puzzles_str), "VerifyMethod":"FoldItVerify"} data = {'SetPuzzlesCompleteVerify': json.dumps(verify), 'SetPuzzlesComplete': puzzles_str} request = self.make_request(data) response = foldit_ops(request) self.assertEqual(response.status_code, 200) return response @staticmethod def set_puzzle_complete_response(values): return json.dumps([{"OperationID":"SetPuzzlesComplete", "Value": values}]) def test_SetPlayerPuzzlesComplete(self): puzzles = [ {"PuzzleID": 13, "Set": 1, "SubSet": 2}, {"PuzzleID": 53524, "Set": 1, "SubSet": 1} ] response = self.make_puzzles_complete_request(puzzles) self.assertEqual(response.content, self.set_puzzle_complete_response([13, 53524])) def test_SetPlayerPuzzlesComplete_multiple(self): """Check that state is stored properly""" puzzles = [ {"PuzzleID": 13, "Set": 1, "SubSet": 2}, {"PuzzleID": 53524, "Set": 1, "SubSet": 1} ] response = self.make_puzzles_complete_request(puzzles) self.assertEqual(response.content, self.set_puzzle_complete_response([13, 53524])) puzzles = [ {"PuzzleID": 14, "Set": 1, "SubSet": 3}, {"PuzzleID": 15, "Set": 1, "SubSet": 1} ] response = self.make_puzzles_complete_request(puzzles) self.assertEqual(response.content, self.set_puzzle_complete_response([13, 14, 15, 53524])) def test_SetPlayerPuzzlesComplete_level_complete(self): """Check that the level complete function works""" puzzles = [ {"PuzzleID": 13, "Set": 1, "SubSet": 2}, {"PuzzleID": 53524, "Set": 1, "SubSet": 1} ] response = self.make_puzzles_complete_request(puzzles) self.assertEqual(response.content, self.set_puzzle_complete_response([13, 53524])) puzzles = [ {"PuzzleID": 14, "Set": 1, "SubSet": 3}, {"PuzzleID": 15, "Set": 1, "SubSet": 1} ] response = self.make_puzzles_complete_request(puzzles) self.assertEqual(response.content, self.set_puzzle_complete_response([13, 14, 15, 53524])) is_complete = partial( PuzzleComplete.is_level_complete, self.unique_user_id) self.assertTrue(is_complete(1, 1)) self.assertTrue(is_complete(1, 3)) self.assertTrue(is_complete(1, 2)) self.assertFalse(is_complete(4, 5)) puzzles = [ {"PuzzleID": 74, "Set": 4, "SubSet": 5} ] response = self.make_puzzles_complete_request(puzzles) self.assertTrue(is_complete(4, 5)) # Now check due dates self.assertTrue(is_complete(1, 1, due=self.tomorrow)) self.assertFalse(is_complete(1, 1, due=self.yesterday)) def test_SetPlayerPuzzlesComplete_error(self): puzzles = [ {"PuzzleID": 13, "Set": 1, "SubSet": 2}, {"PuzzleID": 53524, "Set": 1, "SubSet": 1} ] puzzles_str = json.dumps(puzzles) verify = {"Verify": verify_code(self.user.email, puzzles_str + "x"), "VerifyMethod":"FoldItVerify"} data = {'SetPuzzlesCompleteVerify': json.dumps(verify), 'SetPuzzlesComplete': puzzles_str} request = self.make_request(data) response = foldit_ops(request) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) self.assertEqual(response.content, json.dumps([{ "OperationID": "SetPuzzlesComplete", "Success": "false", "ErrorString": "Verification failed", "ErrorCode": "VerifyFailed"}]))