def test_request_version(self): with self.assertRaises(HTTPUnauthorized): response = ApiView( self.invalid_password_request()).request_version() self.assertTrue('error' in response) response = ApiView(self.correct_password_request()).request_version() self.assertEqual(WORKER_VERSION, response['version'])
def test_beat(self): with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).beat() self.assertTrue("error" in response) request = self.correct_password_request({ "run_id": self.run_id, "task_id": self.task_id }) response = ApiView(request).beat() self.assertEqual("Pleased to hear from you...", response)
def test_beat(self): with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).beat() self.assertTrue("error" in response) request = self.correct_password_request({ "run_id": self.run_id, "task_id": self.task_id }) response = ApiView(request).beat() print(response) self.assertEqual("JoeUserWorker-7cores-unique key", response)
def test_stop_run(self): run_id = new_run(self, add_tasks=1) with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).stop_run() self.assertTrue("error" in response) print(response["error"]) run = self.rundb.get_run(run_id) self.assertFalse(run["finished"]) message = "/api/stop_run request" request = self.correct_password_request( {"run_id": run_id, "task_id": 0, "message": message} ) with self.assertRaises(HTTPUnauthorized): response = ApiView(request).stop_run() self.assertTrue(error in response) sefl.assertFalse(run["tasks"][0]["active"]) self.rundb.userdb.user_cache.update_one( {"username": self.username}, {"$set": {"cpu_hours": 10000}} ) response = ApiView(request).stop_run() response.pop("duration", None) self.assertTrue(response == {}) # run = self.rundb.get_run(run_id) self.assertTrue(run["finished"]) self.assertTrue(message in run["stop_reason"])
def test_upload_pgn(self): run_id = new_run(self, add_tasks=1) pgn_text = "1. e4 e5 2. d4 d5" request = self.correct_password_request( { "run_id": run_id, "task_id": 0, "pgn": base64.b64encode( zlib.compress(pgn_text.encode("utf-8")) ).decode(), } ) response = ApiView(request).upload_pgn() response.pop("duration", None) self.assertTrue(response == {}) pgn_filename_prefix = "{}-{}".format(run_id, 0) pgn = self.rundb.get_pgn(pgn_filename_prefix) self.assertEqual(pgn, pgn_text) self.rundb.pgndb.delete_one({"run_id": pgn_filename_prefix})
def test_request_spsa(self): request = self.correct_password_request({ 'run_id': self.run_id, 'task_id': 0, }) response = ApiView(request).request_spsa() self.assertFalse(response['task_alive']) run = self.rundb.get_run(self.run_id) run['args']['spsa'] = { 'iter': 1, 'num_iter': 10, 'alpha': 1, 'gamma': 1, 'A': 1, 'params': [{ 'name': 'param name', 'a': 1, 'c': 1, 'theta': 1, 'min': 0, 'max': 100, }] } run['tasks'][self.task_id]['pending'] = True run['tasks'][self.task_id]['active'] = True self.rundb.buffer(run, True) request = self.correct_password_request({ 'run_id': self.run_id, 'task_id': self.task_id, }) response = ApiView(request).request_spsa() self.assertTrue(response['task_alive']) self.assertTrue(response['w_params'] is not None) self.assertTrue(response['b_params'] is not None)
def test_request_spsa(self): request = self.correct_password_request({ "run_id": self.run_id, "task_id": 0 }) response = ApiView(request).request_spsa() self.assertFalse(response["task_alive"]) run = self.rundb.get_run(self.run_id) run["args"]["spsa"] = { "iter": 1, "num_iter": 10, "alpha": 1, "gamma": 1, "A": 1, "params": [{ "name": "param name", "a": 1, "c": 1, "theta": 1, "min": 0, "max": 100 }], } run["tasks"][self.task_id]["pending"] = True run["tasks"][self.task_id]["active"] = True self.rundb.buffer(run, True) request = self.correct_password_request({ "run_id": self.run_id, "task_id": self.task_id }) response = ApiView(request).request_spsa() self.assertTrue(response["task_alive"]) self.assertTrue(response["w_params"] is not None) self.assertTrue(response["b_params"] is not None)
def test_failed_task(self): request = self.correct_password_request({ "run_id": self.run_id, "task_id": 0 }) response = ApiView(request).failed_task() self.assertFalse(response["task_alive"]) run = self.rundb.get_run(self.run_id) run["tasks"][self.task_id]["active"] = True run["tasks"][self.task_id]["worker_info"] = self.worker_info self.rundb.buffer(run, True) run = self.rundb.get_run(self.run_id) self.assertTrue(run["tasks"][self.task_id]["active"]) request = self.correct_password_request({ "run_id": self.run_id, "task_id": self.task_id }) response = ApiView(request).failed_task() self.assertTrue(not response) self.assertFalse(run["tasks"][self.task_id]["active"])
def test_failed_task(self): request = self.correct_password_request({ 'run_id': self.run_id, 'task_id': 0, }) response = ApiView(request).failed_task() self.assertFalse(response['task_alive']) run = self.rundb.get_run(self.run_id) run['tasks'][self.task_id]['active'] = True run['tasks'][self.task_id]['worker_info'] = self.worker_info self.rundb.buffer(run, True) run = self.rundb.get_run(self.run_id) self.assertTrue(run['tasks'][self.task_id]['active']) request = self.correct_password_request({ 'run_id': self.run_id, 'task_id': self.task_id, }) response = ApiView(request).failed_task() self.assertTrue(not response) self.assertFalse(run['tasks'][self.task_id]['active'])
def test_stop_run(self): request = testing.DummyRequest(rundb=self.rundb, userdb=self.rundb.userdb, actiondb=self.rundb.actiondb, method='POST', json_body={ 'username': '******', 'password': '******', 'run_id': self.run_id, 'message': 'travis' }) response = ApiView(request).stop_run() self.assertEqual(response, {}) run = request.rundb.get_run(request.json_body['run_id']) self.assertEqual(run['stop_reason'], 'travis')
def test_upload_pgn(self): pgn_text = '1. e4 e5 2. d4 d5' request = self.correct_password_request({ 'run_id': self.run_id, 'task_id': self.task_id, 'pgn': base64.b64encode(zlib.compress(pgn_text.encode('utf-8'))).decode() }) response = ApiView(request).upload_pgn() self.assertTrue(not response) pgn_filename_prefix = '{}-{}'.format(self.run_id, self.task_id) pgn = self.rundb.get_pgn(pgn_filename_prefix) self.assertEqual(pgn, pgn_text) self.rundb.pgndb.delete_one({'run_id': pgn_filename_prefix})
def test_stop_run(self): request = testing.DummyRequest( rundb=self.rundb, userdb=self.rundb.userdb, actiondb=self.rundb.actiondb, method="POST", json_body={ "username": "******", "password": "******", "run_id": self.run_id, "message": "travis", }, ) response = ApiView(request).stop_run() self.assertEqual(response, {}) run = request.rundb.get_run(request.json_body["run_id"]) self.assertEqual(run["stop_reason"], "travis")
def test_upload_pgn(self): pgn_text = "1. e4 e5 2. d4 d5" request = self.correct_password_request({ "run_id": self.run_id, "task_id": self.task_id, "pgn": base64.b64encode(zlib.compress(pgn_text.encode("utf-8"))).decode(), }) response = ApiView(request).upload_pgn() self.assertTrue(not response) pgn_filename_prefix = "{}-{}".format(self.run_id, self.task_id) pgn = self.rundb.get_pgn(pgn_filename_prefix) self.assertEqual(pgn, pgn_text) self.rundb.pgndb.delete_one({"run_id": pgn_filename_prefix})
def test_beat(self): run_id = new_run(self, add_tasks=1) with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).beat() self.assertTrue("error" in response) print(response["error"]) request = self.correct_password_request({"run_id": run_id, "task_id": 0}) response = ApiView(request).beat() response.pop("duration", None) self.assertEqual(response, {})
def test_request_spsa(self): run_id = new_run(self, add_tasks=1) run = self.rundb.get_run(run_id) run["args"]["spsa"] = { "iter": 1, "num_iter": 10, "alpha": 1, "gamma": 1, "A": 1, "params": [ {"name": "param name", "a": 1, "c": 1, "theta": 1, "min": 0, "max": 100} ], } request = self.correct_password_request({"run_id": run_id, "task_id": 0}) response = ApiView(request).request_spsa() self.assertTrue(response["task_alive"]) self.assertTrue(response["w_params"] is not None) self.assertTrue(response["b_params"] is not None)
def test_auto_purge_runs(self): run = self.rundb.get_run(self.run_id) # Request task 1 of 2 request = self.correct_password_request( {"worker_info": self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response["run"]["_id"], str(run["_id"])) self.assertEqual(response["task_id"], 0) # Request task 2 of 2 request = self.correct_password_request( {"worker_info": self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response["run"]["_id"], str(run["_id"])) self.assertEqual(response["task_id"], 1) n_wins = self.rundb.chunk_size / 5 n_losses = self.rundb.chunk_size / 5 n_draws = self.rundb.chunk_size * 3 / 5 # Finish task 1 of 2 request = self.correct_password_request({ "worker_info": self.worker_info, "run_id": self.run_id, "task_id": 0, "stats": { "wins": n_wins, "draws": n_draws, "losses": n_losses, "crashes": 0, }, }) response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) run = self.rundb.get_run(self.run_id) self.assertFalse(run["finished"]) # Finish task 2 of 2 request = self.correct_password_request({ "worker_info": self.worker_info, "run_id": self.run_id, "task_id": 1, "stats": { "wins": n_wins, "draws": n_draws, "losses": n_losses, "crashes": 0, }, }) response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) # The run should be marked as finished after the last task completes run = self.rundb.get_run(self.run_id) self.assertTrue(run["finished"]) self.assertFalse(run["results_stale"]) self.assertTrue( all([not t["pending"] and not t["active"] for t in run["tasks"]])) self.assertTrue( "Total: {}".format(self.rundb.chunk_size * 2) in run["results_info"]["info"][1])
def test_get_elo(self): run_id = new_run(self) request = DummyRequest(rundb=self.rundb, matchdict={"id": run_id}) response = ApiView(request).get_elo() # /api/get_elo only works for SPRT self.assertFalse(response)
def test_update_task(self): run_id = new_run(self, add_tasks=1) run = self.rundb.get_run(run_id) self.assertFalse(run["results_stale"]) # Request fails if username/password is invalid with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).update_task() self.assertTrue("error" in response) print(response["error"]) # Task is active after calling /api/update_task with the first set of results request = self.correct_password_request( { "run_id": run_id, "task_id": 0, "stats": { "wins": 2, "draws": 0, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, 0, 0, 1], }, } ) response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) self.assertFalse(self.rundb.get_run(run_id)["results_stale"]) # Task is still active cs = self.chunk_size w, d, l = cs // 2 - 10, cs // 2, 0 request.json_body["stats"] = { "wins": w, "draws": d, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, d // 2, 0, w // 2], } response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) self.assertFalse(self.rundb.get_run(run_id)["results_stale"]) # Task is still active. Odd update. request.json_body["stats"] = { "wins": w + 1, "draws": d, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, d // 2, 0, w // 2], } response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) request.json_body["stats"] = { "wins": w + 2, "draws": d, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, d // 2, 0, w // 2 + 1], } response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) response = ApiView(request).update_task() self.assertTrue("info" in response) print(response["info"]) # revive the task run["tasks"][0]["active"] = True self.rundb.buffer(run, True) request.json_body["stats"] = { "wins": w + 2, "draws": d, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, d // 2, 0, w // 2 + 1], } response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) # Go back in time request.json_body["stats"] = { "wins": w, "draws": d, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, d // 2, 0, w // 2], } response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) # revive the task run["tasks"][0]["active"] = True self.rundb.buffer(run, True) # Task is finished when calling /api/update_task with results where the number of # games played is the same as the number of games in the task task_num_games = run["tasks"][0]["num_games"] request.json_body["stats"] = { "wins": task_num_games, "draws": 0, "losses": 0, "crashes": 0, "time_losses": 0, "pentanomial": [0, 0, 0, 0, task_num_games // 2], } response = ApiView(request).update_task() self.assertFalse(self.rundb.get_run(run_id)["results_stale"]) self.assertFalse(response["task_alive"]) run = self.rundb.get_run(run_id) task = run["tasks"][0] self.assertFalse(task["active"])
def test_update_task(self): self.assertFalse(self.rundb.get_run(self.run_id)["results_stale"]) # Request fails if username/password is invalid with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).update_task() self.assertTrue("error" in response) # Prepare a pending task that will be assigned to this worker run = self.rundb.get_run(self.run_id) run["tasks"][self.task_id] = { "num_games": self.rundb.chunk_size, "pending": True, "active": False, } if run["args"].get("spsa"): del run["args"]["spsa"] self.rundb.buffer(run, True) # Calling /api/request_task assigns this task to the worker request = self.correct_password_request( {"worker_info": self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response["run"]["_id"], str(run["_id"])) # Task is active after calling /api/update_task with the first set of results request = self.correct_password_request({ "worker_info": self.worker_info, "run_id": self.run_id, "task_id": self.task_id, "stats": { "wins": 2, "draws": 0, "losses": 0, "crashes": 0 }, }) response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) self.assertTrue(self.rundb.get_run(self.run_id)["results_stale"]) # Task is still active cs = self.rundb.chunk_size w, d, l = cs / 2 - 10, cs / 2, 0 request.json_body["stats"] = { "wins": w, "draws": d, "losses": l, "crashes": 0 } response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) self.assertTrue(self.rundb.get_run(self.run_id)["results_stale"]) # Task is still active. Odd update. request.json_body["stats"] = { "wins": w + 1, "draws": d, "losses": 0, "crashes": 0, } response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) # Task_alive is a misnomer... request.json_body["stats"] = { "wins": w + 2, "draws": d, "losses": 0, "crashes": 0, } response = ApiView(request).update_task() self.assertTrue(response["task_alive"]) # Go back in time request.json_body["stats"] = { "wins": w, "draws": d, "losses": 0, "crashes": 0 } response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) # Task is finished when calling /api/update_task with results where the number of # games played is the same as the number of games in the task task_num_games = run["tasks"][self.task_id]["num_games"] request.json_body["stats"] = { "wins": task_num_games, "draws": 0, "losses": 0, "crashes": 0, } response = ApiView(request).update_task() self.assertFalse(self.rundb.get_run(self.run_id)["results_stale"]) self.assertFalse(response["task_alive"]) run = self.rundb.get_run(self.run_id) task = run["tasks"][self.task_id] self.assertFalse(task["pending"]) self.assertFalse(task["active"])
def test_get_elo(self): request = DummyRequest(rundb=self.rundb, matchdict={"id": self.run_id}) response = ApiView(request).get_elo() self.assertTrue(not response)
def test_get_run(self): request = DummyRequest(rundb=self.rundb, matchdict={"id": self.run_id}) response = ApiView(request).get_run() self.assertEqual(self.run_id, response["_id"])
def test_get_active_runs(self): request = DummyRequest(rundb=self.rundb) response = ApiView(request).active_runs() self.assertTrue(self.run_id in response)
def test_auto_purge_runs(self): run = self.rundb.get_run(self.run_id) # Request task 1 of 2 request = self.correct_password_request( {'worker_info': self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response['run']['_id'], str(run['_id'])) self.assertEqual(response['task_id'], 0) # Request task 2 of 2 request = self.correct_password_request( {'worker_info': self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response['run']['_id'], str(run['_id'])) self.assertEqual(response['task_id'], 1) n_wins = self.rundb.chunk_size / 5 n_losses = self.rundb.chunk_size / 5 n_draws = self.rundb.chunk_size * 3 / 5 # Finish task 1 of 2 request = self.correct_password_request({ 'worker_info': self.worker_info, 'run_id': self.run_id, 'task_id': 0, 'stats': { 'wins': n_wins, 'draws': n_draws, 'losses': n_losses, 'crashes': 0 } }) response = ApiView(request).update_task() self.assertFalse(response['task_alive']) run = self.rundb.get_run(self.run_id) self.assertFalse(run['finished']) # Finish task 2 of 2 request = self.correct_password_request({ 'worker_info': self.worker_info, 'run_id': self.run_id, 'task_id': 1, 'stats': { 'wins': n_wins, 'draws': n_draws, 'losses': n_losses, 'crashes': 0 } }) response = ApiView(request).update_task() self.assertFalse(response['task_alive']) # The run should be marked as finished after the last task completes run = self.rundb.get_run(self.run_id) self.assertTrue(run['finished']) self.assertFalse(run['results_stale']) self.assertTrue( all([not t['pending'] and not t['active'] for t in run['tasks']])) self.assertTrue( 'Total: {}'.format(self.rundb.chunk_size * 2) in run['results_info']['info'][1])
def test_failed_task(self): run_id = new_run(self, add_tasks=1) run = self.rundb.get_run(run_id) # Request fails if username/password is invalid request = self.invalid_password_request() with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).update_task() self.assertTrue("error" in response) print(response["error"]) self.assertTrue(run["tasks"][0]["active"]) message = "Sorry but I can't run this" request = self.correct_password_request( {"run_id": run_id, "task_id": 0, "message": message} ) response = ApiView(request).failed_task() response.pop("duration", None) self.assertEqual(response, {}) self.assertFalse(run["tasks"][0]["active"]) request = self.correct_password_request({"run_id": run_id, "task_id": 0}) response = ApiView(request).failed_task() self.assertTrue("info" in response) print(response["info"]) self.assertFalse(run["tasks"][0]["active"]) # revive task run["tasks"][0]["active"] = True self.rundb.buffer(run, True) request = self.correct_password_request( {"run_id": run_id, "task_id": 0, "message": message} ) response = ApiView(request).failed_task() response.pop("duration", None) self.assertTrue(response == {}) self.assertFalse(run["tasks"][0]["active"])
def test_auto_purge_runs(self): stop_all_runs(self) run_id = new_run(self) run = self.rundb.get_run(run_id) num_games = 600 run["args"]["num_games"] = num_games self.rundb.buffer(run, True) # Request task 1 of 2 request = self.correct_password_request() response = ApiView(request).request_task() self.assertEqual(response["run"]["_id"], str(run["_id"])) self.assertEqual(response["task_id"], 0) task1 = self.rundb.get_run(run_id)["tasks"][0] task_size1 = task1["num_games"] # Request task 2 of 2 request = self.correct_password_request() response = ApiView(request).request_task() self.assertEqual(response["run"]["_id"], str(run["_id"])) self.assertEqual(response["task_id"], 1) task2 = self.rundb.get_run(run_id)["tasks"][1] task_size2 = task2["num_games"] task_start2 = task2["start"] self.assertEqual(task_start2, task_size1) # Finish task 1 of 2 n_wins = task_size1 // 5 n_losses = task_size1 // 5 n_draws = task_size1 - n_wins - n_losses request = self.correct_password_request( { "run_id": run_id, "task_id": 0, "stats": { "wins": n_wins, "draws": n_draws, "losses": n_losses, "crashes": 0, "time_losses": 0, "pentanomial": [n_losses // 2, 0, n_draws // 2, 0, n_wins // 2], }, } ) response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) run = self.rundb.get_run(run_id) self.assertFalse(run["finished"]) # Finish task 2 of 2 n_wins = task_size2 // 5 n_losses = task_size2 // 5 n_draws = task_size2 - n_wins - n_losses request = self.correct_password_request( { "run_id": run_id, "task_id": 1, "stats": { "wins": n_wins, "draws": n_draws, "losses": n_losses, "crashes": 0, "time_losses": 0, "pentanomial": [n_losses // 2, 0, n_draws // 2, 0, n_wins // 2], }, } ) response = ApiView(request).update_task() self.assertFalse(response["task_alive"]) # The run should be marked as finished after the last task completes run = self.rundb.get_run(run_id) self.assertTrue(run["finished"]) self.assertFalse(run["results_stale"]) self.assertTrue(all([not t["active"] for t in run["tasks"]])) self.assertTrue("Total: {}".format(num_games) in run["results_info"]["info"][1])
def test_update_task(self): self.assertFalse(self.rundb.get_run(self.run_id)['results_stale']) # Request fails if username/password is invalid with self.assertRaises(HTTPUnauthorized): response = ApiView(self.invalid_password_request()).update_task() self.assertTrue('error' in response) # Prepare a pending task that will be assigned to this worker run = self.rundb.get_run(self.run_id) run['tasks'][self.task_id] = { 'num_games': self.rundb.chunk_size, 'pending': True, 'active': False } if run['args'].get('spsa'): del run['args']['spsa'] self.rundb.buffer(run, True) # Calling /api/request_task assigns this task to the worker request = self.correct_password_request( {'worker_info': self.worker_info}) response = ApiView(request).request_task() self.assertEqual(response['run']['_id'], str(run['_id'])) # Task is active after calling /api/update_task with the first set of results request = self.correct_password_request({ 'worker_info': self.worker_info, 'run_id': self.run_id, 'task_id': self.task_id, 'stats': { 'wins': 2, 'draws': 0, 'losses': 0, 'crashes': 0 } }) response = ApiView(request).update_task() self.assertTrue(response['task_alive']) self.assertTrue(self.rundb.get_run(self.run_id)['results_stale']) # Task is still active cs = self.rundb.chunk_size w, d, l = cs / 2 - 10, cs / 2, 0 request.json_body['stats'] = { 'wins': w, 'draws': d, 'losses': l, 'crashes': 0 } response = ApiView(request).update_task() self.assertTrue(response['task_alive']) self.assertTrue(self.rundb.get_run(self.run_id)['results_stale']) # Task is still active. Odd update. request.json_body['stats'] = { 'wins': w + 1, 'draws': d, 'losses': 0, 'crashes': 0 } response = ApiView(request).update_task() self.assertFalse(response['task_alive']) # Task_alive is a misnomer... request.json_body['stats'] = { 'wins': w + 2, 'draws': d, 'losses': 0, 'crashes': 0 } response = ApiView(request).update_task() self.assertTrue(response['task_alive']) # Go back in time request.json_body['stats'] = { 'wins': w, 'draws': d, 'losses': 0, 'crashes': 0 } response = ApiView(request).update_task() self.assertFalse(response['task_alive']) # Task is finished when calling /api/update_task with results where the number of # games played is the same as the number of games in the task task_num_games = run['tasks'][self.task_id]['num_games'] request.json_body['stats'] = { 'wins': task_num_games, 'draws': 0, 'losses': 0, 'crashes': 0 } response = ApiView(request).update_task() self.assertFalse(self.rundb.get_run(self.run_id)['results_stale']) self.assertFalse(response['task_alive']) run = self.rundb.get_run(self.run_id) task = run['tasks'][self.task_id] self.assertFalse(task['pending']) self.assertFalse(task['active'])