def upload_output(self, input, file): """ POST /upload_output """ output_id = Database.gen_id() try: path = StorageManager.new_output_file(output_id, file["name"]) except ValueError: BaseHandler.raise_exc(BadRequest, "INVALID_FILENAME", "The provided file has an invalid name") StorageManager.save_file(path, file["content"]) file_size = StorageManager.get_file_size(path) try: result = ContestManager.evaluate_output(input["task"], input["path"], path) except: BaseHandler.raise_exc(InternalServerError, "INTERNAL_ERROR", "Failed to evaluate the output") Database.add_output(output_id, input["id"], path, file_size, result) Logger.info( "UPLOAD", "User %s has uploaded the output %s" % (input["token"], output_id)) return InfoHandler.patch_output(Database.get_output(output_id))
def test_patch_submission(self, mock): Utils.start_contest() submission = { "id": "subid", "nested_item": 42, "output_result": b'{"feedback":123}' } res = InfoHandler.patch_submission(submission) self.assertEqual("subid", res["id"]) self.assertEqual(42, res["nested"]["item"]) self.assertEqual(123, res["feedback"]) self.assertEqual("outputid", res["output"]["id"])
def submit(self, output, source): """ POST /submit """ input = Database.get_input(output["input"]) if input is None: Logger.warning("DB_CONSISTENCY_ERROR", "Input %s not found in the db" % output["input"]) self.raise_exc(BadRequest, "WRONG_INPUT", "The provided input in invalid") if output["input"] != source["input"]: Logger.warning("POSSIBLE_CHEAT", "Trying to submit wrong pair source-output") self.raise_exc(Forbidden, "WRONG_OUTPUT_SOURCE", "The provided pair of source-output is invalid") score = ContestHandler.compute_score(input["task"], output["result"]) Database.begin() try: submission_id = Database.gen_id() if not Database.add_submission(submission_id, input["id"], output["id"], source["id"], score, autocommit=False): self.raise_exc(BadRequest, "INTERNAL_ERROR", "Error inserting the submission") ContestHandler.update_user_score(input["token"], input["task"], score) Database.set_user_attempt(input["token"], input["task"], None, autocommit=False) Database.commit() except sqlite3.IntegrityError as ex: Database.rollback() # provide a better error message if the input has already been # submitted if "UNIQUE constraint failed: submissions.input" in str(ex): self.raise_exc(Forbidden, "ALREADY_SUBMITTED", "This input has already been submitted") raise except: Database.rollback() raise Logger.info( "CONTEST", "User %s has submitted %s on %s" % (input["token"], submission_id, input["task"])) return InfoHandler.patch_submission( Database.get_submission(submission_id))
def test_patch_output(self): Utils.start_contest() output = { "id": "outputid", "date": 1234, "path": "/path", "size": 42, "result": b'{"validation":42}' } res = InfoHandler.patch_output(output) self.assertEqual("outputid", res["id"]), self.assertEqual( datetime.datetime.fromtimestamp(1234).isoformat(), res["date"]) self.assertEqual("/path", res["path"]) self.assertEqual(42, res["validation"]) self.assertEqual(42, res["size"])
def __init__(self): self.handlers = { "contest": ContestHandler(), "info": InfoHandler(), "upload": UploadHandler(), "admin": AdminHandler(), } # The router tries to match the rules, the endpoint MUST be a string # with this format # CONTROLLER#ACTION # Where CONTROLLER is an handler registered in self.handlers and # ACTION is a valid # method of that handler self.router = Map([ Rule("/contest", methods=["GET"], endpoint="info#get_contest"), Rule("/input/<input_id>", methods=["GET"], endpoint="info#get_input"), Rule("/output/<output_id>", methods=["GET"], endpoint="info#get_output"), Rule("/source/<source_id>", methods=["GET"], endpoint="info#get_source"), Rule( "/submission/<submission_id>", methods=["GET"], endpoint="info#get_submission", ), Rule("/user/<token>", methods=["GET"], endpoint="info#get_user"), Rule( "/user/<token>/submissions/<task>", methods=["GET"], endpoint="info#get_submissions", ), Rule( "/generate_input", methods=["POST"], endpoint="contest#generate_input", ), Rule("/submit", methods=["POST"], endpoint="contest#submit"), Rule( "/internet_detected", methods=["POST"], endpoint="contest#internet_detected", ), Rule("/upload_source", methods=["POST"], endpoint="upload#upload_source"), Rule("/upload_output", methods=["POST"], endpoint="upload#upload_output"), Rule("/admin/upload_pack", methods=["POST"], endpoint="admin#upload_pack"), Rule( "/admin/download_results", methods=["POST"], endpoint="admin#download_results", ), Rule("/admin/login", methods=["POST"], endpoint="admin#login"), Rule("/admin/log", methods=["POST"], endpoint="admin#log"), Rule("/admin/append_log", methods=["POST"], endpoint="admin#append_log"), Rule("/admin/start", methods=["POST"], endpoint="admin#start"), Rule( "/admin/set_extra_time", methods=["POST"], endpoint="admin#set_extra_time", ), Rule("/admin/status", methods=["POST"], endpoint="admin#status"), Rule("/admin/pack_status", methods=["GET"], endpoint="admin#pack_status"), Rule("/admin/user_list", methods=["POST"], endpoint="admin#user_list"), Rule( "/admin/drop_contest", methods=["POST"], endpoint="admin#drop_contest", ), ])
def setUp(self): Utils.prepare_test() self.handler = InfoHandler() self.log_backup = Logger.LOG_LEVEL Logger.LOG_LEVEL = 9001 # disable the logs
class TestInfoHandler(unittest.TestCase): def setUp(self): Utils.prepare_test() self.handler = InfoHandler() self.log_backup = Logger.LOG_LEVEL Logger.LOG_LEVEL = 9001 # disable the logs def tearDown(self): Logger.LOG_LEVEL = self.log_backup def test_get_contest_not_started(self): res = self.handler.get_contest() self.assertFalse(res["has_started"]) def test_contest_started(self): Database.add_task("poldo", "", "", 42, 1) Database.set_meta("start_time", 1234) res = self.handler.get_contest() self.assertTrue(res["has_started"]) self.assertEqual( datetime.datetime.fromtimestamp(1234).isoformat(), res["start_time"]) self.assertEqual(1, len(res["tasks"])) self.assertEqual("poldo", res["tasks"][0]["name"]) def test_get_input(self): Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Utils.start_contest() res = self.handler.get_input(input_id="inputid", _ip="1.1.1.1") self.assertEqual("inputid", res["id"]) self.assertEqual("token", res["token"]) self.assertEqual("poldo", res["task"]) def test_get_input_invalid_id(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_input(input_id="invalid input", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such input", response) def test_get_output(self): Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.add_output("outputid", "inputid", "/path", 42, b'{"validation":42}') Utils.start_contest() res = self.handler.get_output(output_id="outputid", _ip="1.1.1.1") self.assertEqual("outputid", res["id"]) self.assertEqual(42, res["validation"]) def test_get_output_invalid_id(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_output(output_id="invalid output", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such output", response) def test_get_source(self): Utils.start_contest() Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.add_source("sourceid", "inputid", "/path", 42) res = self.handler.get_source(source_id="sourceid", _ip="1.1.1.1") self.assertEqual("sourceid", res["id"]) def test_get_source_invalid_id(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_source(source_id="invalid source", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such source", response) def test_get_submission(self): Utils.start_contest() Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.add_output("outputid", "inputid", "/path", 42, b'{"validation":42,"feedback":42}') Database.add_source("sourceid", "inputid", "/path", 42) Database.add_submission("subid", "inputid", "outputid", "sourceid", 42) res = self.handler.get_submission(submission_id="subid", _ip="1.1.1.1") self.assertEqual("subid", res["id"]) self.assertEqual("inputid", res["input"]["id"]) self.assertEqual("outputid", res["output"]["id"]) self.assertEqual("sourceid", res["source"]["id"]) self.assertEqual(42, res["feedback"]) def test_get_submission_invalid_id(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_submission(submission_id="invalid submission", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such submission", response) def test_get_user_invalid_token(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_user(token="invalid token", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such user", response) def test_get_user(self): now = int(datetime.datetime.now().timestamp()) Database.set_meta("start_time", now) Database.set_meta("contest_duration", 1000) Database.set_meta("extra_time", 50) Database.add_user("token", "", "") Database.set_extra_time("token", 30) Database.add_task("poldo", "", "", 42, 1) Database.add_user_task("token", "poldo") Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.set_user_attempt("token", "poldo", 1) res = self.handler.get_user(token="token", _ip="1.1.1.1") end_time = datetime.datetime.fromtimestamp(now + 1080).strftime( '%Y-%m-%dT%H:%M:%S') self.assertEqual(end_time, res["end_time"]) self.assertEqual("poldo", res["tasks"]["poldo"]["name"]) self.assertEqual("inputid", res["tasks"]["poldo"]["current_input"]["id"]) def test_get_user_windowed(self): now = int(datetime.datetime.now().timestamp()) Database.set_meta("start_time", now) Database.set_meta("contest_duration", 1000) Database.set_meta("window_duration", 100) Database.add_user("token", "", "") Database.set_start_delay("token", 10) Database.add_task("poldo", "", "", 42, 1) Database.add_user_task("token", "poldo") Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.set_user_attempt("token", "poldo", 1) res = self.handler.get_user(token="token", _ip="1.1.1.1") end_time = datetime.datetime.fromtimestamp(now + 110).strftime( '%Y-%m-%dT%H:%M:%S') self.assertEqual(end_time, res["end_time"]) def test_get_user_windowed_almost_finished(self): now = int(datetime.datetime.now().timestamp()) Database.set_meta("start_time", now - 90) Database.set_meta("contest_duration", 1000) Database.set_meta("window_duration", 100) Database.add_user("token", "", "") Database.set_start_delay("token", 10) Database.add_task("poldo", "", "", 42, 1) Database.add_user_task("token", "poldo") Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.set_user_attempt("token", "poldo", 1) res = self.handler.get_user(token="token", _ip="1.1.1.1") end_time = datetime.datetime.fromtimestamp(now + 20).strftime( '%Y-%m-%dT%H:%M:%S') self.assertEqual(end_time, res["end_time"]) def test_get_user_windowed_partial_window(self): now = int(datetime.datetime.now().timestamp()) Database.set_meta("start_time", now) Database.set_meta("contest_duration", 1000) Database.set_meta("window_duration", 100) Database.add_user("token", "", "") Database.set_start_delay("token", 990) Database.add_task("poldo", "", "", 42, 1) Database.add_user_task("token", "poldo") Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.set_user_attempt("token", "poldo", 1) res = self.handler.get_user(token="token", _ip="1.1.1.1") end_time = datetime.datetime.fromtimestamp(now + 1000).strftime( '%Y-%m-%dT%H:%M:%S') self.assertEqual(end_time, res["end_time"]) def test_get_user_no_current_attempt(self): Utils.start_contest(since=100, duration=200) Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_user_task("token", "poldo") res = self.handler.get_user(token="token", _ip="1.1.1.1") self.assertEqual(None, res["tasks"]["poldo"]["current_input"]) def test_get_submissions(self): Utils.start_contest() Database.add_user("token", "", "") Database.add_task("poldo", "", "", 42, 1) Database.add_input("inputid", "token", "poldo", 1, "/path", 42) Database.add_output("outputid", "inputid", "/path", 42, b'{"validation":42,"feedback":42}') Database.add_source("sourceid", "inputid", "/path", 42) Database.add_submission("subid", "inputid", "outputid", "sourceid", 42) res = self.handler.get_submissions(token="token", task="poldo", _ip="1.1.1.1") self.assertEqual(1, len(res["items"])) self.assertEqual("subid", res["items"][0]["id"]) def test_get_submissions_invalid_token(self): Utils.start_contest() with self.assertRaises(Forbidden) as ex: self.handler.get_submissions(token="invalid token", task="poldo", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such user", response) def test_get_submissions_invalid_task(self): Utils.start_contest() Database.add_user("token", "", "") with self.assertRaises(Forbidden) as ex: self.handler.get_submissions(token="token", task="invalid task", _ip="1.1.1.1") response = ex.exception.response.data.decode() self.assertIn("No such task", response) @patch("terry.handlers.info_handler.InfoHandler.patch_output", return_value={"id": "outputid"}) def test_patch_submission(self, mock): Utils.start_contest() submission = { "id": "subid", "nested_item": 42, "output_result": b'{"feedback":123}' } res = InfoHandler.patch_submission(submission) self.assertEqual("subid", res["id"]) self.assertEqual(42, res["nested"]["item"]) self.assertEqual(123, res["feedback"]) self.assertEqual("outputid", res["output"]["id"]) def test_patch_output(self): Utils.start_contest() output = { "id": "outputid", "date": 1234, "path": "/path", "size": 42, "result": b'{"validation":42}' } res = InfoHandler.patch_output(output) self.assertEqual("outputid", res["id"]), self.assertEqual( datetime.datetime.fromtimestamp(1234).isoformat(), res["date"]) self.assertEqual("/path", res["path"]) self.assertEqual(42, res["validation"]) self.assertEqual(42, res["size"])