diff --git a/actions/marathonChallenges.js b/actions/marathonChallenges.js index 6569e8ae7..665ab3614 100644 --- a/actions/marathonChallenges.js +++ b/actions/marathonChallenges.js @@ -1,14 +1,17 @@ /* * Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved. * - * @version 1.3 - * @author Sky_, TCSASSEMBLER, freegod, Ghost_141 + * @version 1.4 + * @author Sky_, TCSASSEMBLER, freegod, Ghost_141, hesibo * changes in 1.1: * - implement marathon API * changes in 1.2: * - Use empty result set instead of 404 error in get marathon challenges API. * Changes in 1.3: * - Implement the register marathon match challenge API. + * changes in 1.4: + * - Implement the get marathon match challenge register info API + * - refactor register marathon match challenge API */ "use strict"; var async = require('async'); @@ -104,8 +107,8 @@ function setDateToParams(helper, sqlParams, dateInterval, inputCodePrefix) { } /** -* The API for searching Marathon challenges -*/ + * The API for searching Marathon challenges + */ exports.searchMarathonChallenges = { name: "searchMarathonChallenges", description: "searchMarathonChallenges", @@ -294,7 +297,7 @@ exports.searchMarathonChallenges = { /** * Compute progressResources field for challenge details - * + * * @param {Array} submissions - the submissions. Result of detail_progress_XXX query. * @param {Array} registrants - the registrants. Result of detail_progress_XXX_registrants query. * @param {Array} competitors - the competitors. Result of detail_progress_competitors query. @@ -398,8 +401,8 @@ function computeProgressResources(submissions, registrants, competitors, interva } /** -* The API for getting Marathon challenge -*/ + * The API for getting Marathon challenge + */ exports.getMarathonChallenge = { name: "getMarathonChallenge", description: "getMarathonChallenge", @@ -515,35 +518,36 @@ exports.getMarathonChallenge = { }); } }; - /** - * Register the marathon match challenge. - * @param {Object} api - the api object. - * @param {Object} connection - the connection object. - * @param {Function} next - the callback function. - * @since 1.3 + * perform checking before register marathon or view register info + * + * @param {Object} api - the api object + * @param {Boolean} isRegister - it is register marathon or not + * @param {Object} connection - the connection object + * @param {Object} sqlParams - the sql parameters object + * @param {Function} callback - the callback function + * + * @since 1.4 */ -function registerMarathonMatchChallenge(api, connection, next) { - var helper = api.helper, dbConnectionMap = connection.dbConnectionMap, roundId, sqlParams, isHighSchool, +function preRegisterMarathonCheck(api, isRegister, connection, sqlParams, callback) { + var dbConnectionMap = connection.dbConnectionMap, + roundId = Number(decodeURI(connection.params.roundId).trim()), execQuery = function (name) { return function (cb) { api.dataAccess.executeQuery(name, sqlParams, dbConnectionMap, cb); }; - }, caller = connection.caller; - roundId = Number(decodeURI(connection.params.roundId).trim()); + }; async.waterfall([ function (cb) { - var error = helper.checkPositiveInteger(roundId, 'roundId') || - helper.checkMaxInt(roundId, 'roundId') || - helper.checkMember(connection, 'Authorization information needed or incorrect.'); + var error = api.helper.checkPositiveInteger(roundId, 'roundId') || + api.helper.checkMaxInt(roundId, 'roundId') || + api.helper.checkMember(connection, 'Authorization information needed or incorrect.'); if (error) { cb(error); return; } - sqlParams = { - round_id: roundId, - user_id: caller.userId - }; + sqlParams.round_id = roundId; + sqlParams.user_id = connection.caller.userId; // check async.parallel({ checkResult: execQuery('check_marathon_challenge_register'), @@ -588,7 +592,7 @@ function registerMarathonMatchChallenge(api, connection, next) { } } // If the caller has already reigstered for this challenge. - if (checkResult.is_round_registered) { + if (isRegister && checkResult.is_round_registered) { cb(new BadRequestError('You already registered for this challenge.')); return; } @@ -619,10 +623,39 @@ function registerMarathonMatchChallenge(api, connection, next) { cb(new BadRequestError('There are no more spots available for the round.')); return; } + cb(null, checkResult); + } + ], function (err, checkResult) { + if (isRegister) { + callback(err, checkResult); + } else { + callback(err); + } + }); +} +/** + * Register the marathon match challenge. + * @param {Object} api - the api object. + * @param {Object} connection - the connection object. + * @param {Function} next - the callback function. + * @since 1.3 + */ +function registerMarathonMatchChallenge(api, connection, next) { + var sqlParams = {}, + execQuery = function (name) { + return function (cb) { + api.dataAccess.executeQuery(name, sqlParams, connection.dbConnectionMap, cb); + }; + }; + async.waterfall([ + function (cb) { + preRegisterMarathonCheck(api, true, connection, sqlParams, cb); + }, + function (checkResult, cb) { _.extend(sqlParams, { eligible: 1, - userId: caller.userId, + userId: connection.caller.userId, attended: 'N' }); async.parallel({ @@ -630,7 +663,7 @@ function registerMarathonMatchChallenge(api, connection, next) { roundTerms: execQuery('insert_round_terms_acceptance'), algoRating: function (cbx) { if (!checkResult.is_rated) { - api.dataAccess.executeQuery('add_algo_rating', sqlParams, dbConnectionMap, cbx); + api.dataAccess.executeQuery('add_algo_rating', sqlParams, connection.dbConnectionMap, cbx); return; } cbx(); @@ -642,7 +675,7 @@ function registerMarathonMatchChallenge(api, connection, next) { } ], function (err) { if (err) { - helper.handleError(api, connection, err); + api.helper.handleError(api, connection, err); } else { connection.response = { success: true }; } @@ -677,3 +710,103 @@ exports.registerMarathonChallenge = { } } }; + +/** + * Get marathon match challenge register information. + * + * @param {Object} api - the api object. + * @param {Object} connection - the connection object. + * @param {Function} next - the callback function. + * + * @since 1.4 + */ +function getMarathonChallengeRegInfo(api, connection, next) { + var sqlParams = {}, result = {}, questionIdMapping = {}, index = 0; + async.waterfall([ + function (cb) { + preRegisterMarathonCheck(api, false, connection, sqlParams, cb); + }, + function (cb) { + api.dataAccess.executeQuery('get_marathon_round_term', sqlParams, connection.dbConnectionMap, cb); + }, + function (term, cb) { + if (term.length === 0) { + cb(new NotFoundError('Could not find specified round terms.')); + return; + } + result.term = { + contestName: term[0].contest_name, + roundName: term[0].round_name, + termsContent: term[0].terms_content || '' + }; + api.dataAccess.executeQuery('get_marathon_round_questions', sqlParams, connection.dbConnectionMap, cb); + }, + function (questions, cb) { + sqlParams.question_ids = []; + result.questions = _.map(questions, function (question) { + sqlParams.question_ids.push(question.question_id); + questionIdMapping[question.question_id] = index; + index = index + 1; + return { + id: question.question_id, + style: question.style, + type: question.type, + text: question.text, + answers: [] + }; + }); + if (!_.isEmpty(sqlParams.question_ids)) { + api.dataAccess.executeQuery('get_marathon_round_question_answers', sqlParams, connection.dbConnectionMap, cb); + } else { + cb(null, null); + } + }, + function (answers, cb) { + if (!_.isEmpty(sqlParams.question_ids)) { + answers.forEach(function (answer) { + result.questions[questionIdMapping[answer.question_id]].answers.push({ + id: answer.answer_id, + text: answer.text, + sortOrder: answer.sort_order || -1, + correct: answer.correct === 0 ? false : true + }); + }); + } + cb(); + } + ], function (err) { + if (err) { + api.helper.handleError(api, connection, err); + } else { + connection.response = result; + } + next(connection, true); + }); +} + +/** + * The API for get marathon match challenge register information. + * + * @since 1.4 + */ +exports.getMarathonChallengeRegInfo = { + name: 'getMarathonChallengeRegInfo', + description: 'get marathon match challenge register information', + inputs: { + required: ['roundId'], + optional: [] + }, + blockedConnectionTypes: [], + outputExample: {}, + version: 'v2', + transaction: 'read', // this action is read-only + databases: ['informixoltp', 'common_oltp'], + run: function (api, connection, next) { + if (!connection.dbConnectionMap) { + api.helper.handleNoConnection(api, connection, next); + } else { + api.log('Execute getMarathonChallengeRegInfo#run', 'debug'); + getMarathonChallengeRegInfo(api, connection, next); + } + } +}; diff --git a/apiary.apib b/apiary.apib index 7f84387e6..467462e65 100644 --- a/apiary.apib +++ b/apiary.apib @@ -4440,6 +4440,199 @@ Register a new user. "description":"Servers are up but overloaded. Try again later." } +## Get Marathon Match Challenge Reg Info [/data/marathon/challenges/{roundId}/regInfo] +### Get Marathon Match Challenge Reg Info [GET] + ++ Parameters + + roundId (required, number, `30000000`) ... The challenge round id. + ++ Request + + + Headers + + Authorization : Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZHwxMzI0NTYiLCJleHAiOjEzOTI4MTc4ODQsImF1ZCI6InRvcGNvZGVyIiwiaWF0IjoxMzkyNzU3ODg0fQ.7X2IKkiyyI1ExSM5GNpdhJ8fGGK5-oAjzccX6YL_BKY + ++ Response 200 (application/json) + + { + "term": { + "contestName": "Marathon Match 90", + "roundName": "Round 2001", + "termsContent": "Marathon Match terms content" + }, + "questions": [ + { + "id": 1000, + "style": "Multiple Choice", + "type": "Eligible", + "text": "question 1000", + "answers": [ + { + "id": 1002, + "text": "answer text 1", + "sortOrder": 1, + "correct": true + }, + { + "id": 1001, + "text": "answer text 2", + "sortOrder": 2, + "correct": false + }, + { + "id": 1003, + "text": "answer text 3", + "sortOrder": 3, + "correct": false + } + ] + }, + { + "id": 1001, + "style": "Short Answer", + "type": "Personal", + "text": "question 1001", + "answers": [] + }, + { + "id": 1002, + "style": "Single Choice", + "type": "Eligible", + "text": "question 1002", + "answers": [ + { + "id": 1004, + "text": "answer text 4", + "sortOrder": -1, + "correct": false + }, + { + "id": 1005, + "text": "answer text 5", + "sortOrder": 1, + "correct": true + } + ] + } + ] + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details:":"You are not eligible to participate in this competition." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details:":"Round doesn't exist 30005520." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"In order to participate in this competition, you must register for Event 30005520. Registration is available: here. Please register at the provided URL first and then repeat registration at Marathon Match Active Contests page." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"Registration is not currently open." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"You are not eligible to participate in this competition. Please contact support@topcoder.com if you have any questions." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"Sorry, this round is by invitation only." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"Sorry, you can not register for this round, you must compete in the version of this round that you were invited to." + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"There are no more spots available for the round." + } + ++ Response 401 (application/json) + + { + "name":"Unauthorized", + "value":"401", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"Authorization information needed or incorrect." + } + ++ Response 403 (application/json) + + { + "name":"Forbidden", + "value":"403", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"The user is forbidden to access this endpoint." + } + ++ Response 404 (application/json) + + { + "name":"Not Found", + "value":"404", + "description":"This message will explain why the request is invalid or cannot be served." + "details":"Could not find specified round terms." + } + ++ Response 500 (application/json) + + { + "name":"Internal Server Error", + "value":"500", + "description":"Unknown server error. Please contact support." + } + ++ Response 503 (application/json) + + { + "name":"Service Unavailable", + "value":"503", + "description":"Servers are up but overloaded. Try again later." + } + + ## Register Marathon Match Challenge [/data/marathon/challenges/{roundId}/register] ### Register Marathon Match Challenge [POST] diff --git a/docs/Module Assembly - TopCoder NodeJS Get Marathon Match Challenge Reg Info API.doc b/docs/Module Assembly - TopCoder NodeJS Get Marathon Match Challenge Reg Info API.doc new file mode 100755 index 000000000..89139d80c Binary files /dev/null and b/docs/Module Assembly - TopCoder NodeJS Get Marathon Match Challenge Reg Info API.doc differ diff --git a/queries/get_marathon_round_question_answers b/queries/get_marathon_round_question_answers new file mode 100755 index 000000000..6c01e689a --- /dev/null +++ b/queries/get_marathon_round_question_answers @@ -0,0 +1,9 @@ +SELECT + answer_id, + question_id, + answer_text AS text, + sort_order, + NVL(correct, 0) AS correct +FROM answer +WHERE question_id IN (@question_ids@) +ORDER BY sort_order \ No newline at end of file diff --git a/queries/get_marathon_round_question_answers.json b/queries/get_marathon_round_question_answers.json new file mode 100755 index 000000000..9d37d1c0a --- /dev/null +++ b/queries/get_marathon_round_question_answers.json @@ -0,0 +1,5 @@ +{ + "name" : "get_marathon_round_question_answers", + "db" : "informixoltp", + "sqlfile" : "get_marathon_round_question_answers" +} \ No newline at end of file diff --git a/queries/get_marathon_round_questions b/queries/get_marathon_round_questions new file mode 100755 index 000000000..12119e5b6 --- /dev/null +++ b/queries/get_marathon_round_questions @@ -0,0 +1,15 @@ +SELECT + qu.question_id, + qs.question_style_desc AS style, + qt.question_type_desc AS type, + qu.question_text AS text +FROM + round_question rq, + question qu, + question_style qs, + question_type qt +WHERE rq.question_id = qu.question_id + AND rq.round_id = @round_id@ + AND qs.question_style_id = qu.question_style_id + AND qt.question_type_id = qu.question_type_id +ORDER BY qu.question_id \ No newline at end of file diff --git a/queries/get_marathon_round_questions.json b/queries/get_marathon_round_questions.json new file mode 100755 index 000000000..2c0309b39 --- /dev/null +++ b/queries/get_marathon_round_questions.json @@ -0,0 +1,5 @@ +{ + "name" : "get_marathon_round_questions", + "db" : "informixoltp", + "sqlfile" : "get_marathon_round_questions" +} \ No newline at end of file diff --git a/queries/get_marathon_round_term b/queries/get_marathon_round_term new file mode 100755 index 000000000..1cfbbc957 --- /dev/null +++ b/queries/get_marathon_round_term @@ -0,0 +1,11 @@ +SELECT + terms_content, + ct.name AS contest_name, + rd.name AS round_name +FROM + round_terms rt, + round rd, + contest ct +WHERE rt.round_id = @round_id@ + AND rt.round_id = rd.round_id + AND rd.contest_id = ct.contest_id \ No newline at end of file diff --git a/queries/get_marathon_round_term.json b/queries/get_marathon_round_term.json new file mode 100755 index 000000000..a79c793e9 --- /dev/null +++ b/queries/get_marathon_round_term.json @@ -0,0 +1,5 @@ +{ + "name" : "get_marathon_round_term", + "db" : "informixoltp", + "sqlfile" : "get_marathon_round_term" +} \ No newline at end of file diff --git a/routes.js b/routes.js index adaeac535..356a1e2f6 100755 --- a/routes.js +++ b/routes.js @@ -1,7 +1,7 @@ /* * Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved. * - * @version 1.29 + * @version 1.30 * @author vangavroche, Sky_, muzehyun, kurtrips, Ghost_141, ecnu_haozi, hesibo, LazyChild * Changes in 1.1: * - add routes for search challenges @@ -69,6 +69,8 @@ * - added route for Dosusign get recipient view url * changes in 1.29 * - added route for activate user api + * changes in 1.30 + * - added route for getting marathon match challenge register info api */ /* --------------------- @@ -179,6 +181,7 @@ exports.routes = { { path: "/:apiVersion/data/srm/challenges/:id", action: "getSRMChallenge" }, { path: "/:apiVersion/data/srm/challenges", action: "searchSRMChallenges" }, + { path: "/:apiVersion/data/marathon/challenges/:roundId/regInfo", action: "getMarathonChallengeRegInfo" }, { path: "/:apiVersion/data/marathon/challenges/:id", action: "getMarathonChallenge" }, { path: "/:apiVersion/data/marathon/challenges", action: "searchMarathonChallenges" }, { path: "/:apiVersion/data/marathon/statistics/tops", action: "getMarathonTops" }, diff --git a/test/sqls/marathonRegInfo/common_oltp__clean b/test/sqls/marathonRegInfo/common_oltp__clean new file mode 100755 index 000000000..f840911ef --- /dev/null +++ b/test/sqls/marathonRegInfo/common_oltp__clean @@ -0,0 +1,7 @@ +DELETE FROM user_address_xref WHERE user_id = 300003; +DELETE FROM address WHERE address_id = 2001; +DELETE FROM event_registration WHERE event_id = 2001; +DELETE FROM event WHERE event_id = 2001; +DELETE FROM user_role_xref WHERE login_id IN (300002, 300003); +DELETE FROM security_user WHERE login_id IN (300002, 300003); +DELETE FROM user WHERE user_id IN (300001, 300002, 300003); diff --git a/test/sqls/marathonRegInfo/common_oltp__insert_test_data b/test/sqls/marathonRegInfo/common_oltp__insert_test_data new file mode 100755 index 000000000..c3bbee2c0 --- /dev/null +++ b/test/sqls/marathonRegInfo/common_oltp__insert_test_data @@ -0,0 +1,26 @@ +INSERT INTO user(user_id, handle, status) VALUES(300001, 'testUser300001', 'A'); + +INSERT INTO user(user_id, handle, status) VALUES(300002, 'testUser300002', 'U'); +INSERT INTO security_user(login_id, user_id, password, create_user_id) +VALUES(300002, 'testUser300002', '4EjPjy6o+/C+dqNPnxIy9A==', 0); +INSERT INTO user_role_xref(user_role_id, login_id, role_id, create_user_id, security_status_id) +VALUES(2001, 300002, 1000, 132456, 1); + +INSERT INTO user(user_id, handle, status) VALUES(300003, 'testUser300003', 'A'); +INSERT INTO security_user(login_id, user_id, password, create_user_id) +VALUES(300003, 'testUser300003', '4EjPjy6o+/C+dqNPnxIy9A==', 0); +INSERT INTO user_role_xref(user_role_id, login_id, role_id, create_user_id, security_status_id) +VALUES(2002, 300003, 1000, 132456, 1); +INSERT INTO address(address_id, address_type_id, country_code, create_date, modify_date) +VALUES(2001, 2, 192, current, current); +INSERT INTO user_address_xref(user_id, address_id) VALUES(300003, 2001); + +INSERT INTO event(event_id, event_type_id, event_desc, modify_date) VALUES(2001, 1, 'Test Event 2001', current); +INSERT INTO event_registration(event_id, user_id, eligible_ind, notes, create_date, modify_date) +VALUES(2001, 124764, 0, 'test', current, current); +INSERT INTO event_registration(event_id, user_id, eligible_ind, notes, create_date, modify_date) +VALUES(2001, 124766, 1, 'test', current, current); +INSERT INTO event_registration(event_id, user_id, eligible_ind, notes, create_date, modify_date) +VALUES(2001, 300003, 1, 'test', current, current); +INSERT INTO event_registration(event_id, user_id, eligible_ind, notes, create_date, modify_date) +VALUES(2001, 132456, 1, 'test', current, current); diff --git a/test/sqls/marathonRegInfo/informixoltp__clean b/test/sqls/marathonRegInfo/informixoltp__clean new file mode 100755 index 000000000..98dd846cc --- /dev/null +++ b/test/sqls/marathonRegInfo/informixoltp__clean @@ -0,0 +1,19 @@ +DELETE FROM answer WHERE question_id IN (1000, 1001, 1002); +DELETE FROM round_question WHERE round_id = 2001; +DELETE FROM question WHERE question_id IN (1000, 1001, 1002); + +DELETE FROM round_terms WHERE round_id IN (2001, 2006); + +DELETE FROM round_registration WHERE round_id >= 2001; +DELETE FROM round_terms_acceptance WHERE round_id >= 2001; +DELETE FROM algo_rating WHERE coder_id IN (132456); +DELETE FROM long_comp_result WHERE round_id >= 2001; + +DELETE FROM round_event WHERE round_id IN (2001, 2006, 2007); +DELETE FROM round_segment WHERE round_id IN (2001, 2002, 2003, 2004, 2005, 2006, 2007); +DELETE FROM round_component WHERE round_id IN (2004, 2005); +DELETE FROM invite_list WHERE round_id = 2005; +DELETE FROM round WHERE round_id IN (2001, 2002, 2003, 2004, 2005, 2006, 2007); +DELETE FROM team_coder_xref WHERE coder_id IN (132456); + +DELETE FROM contest WHERE contest_id IN (10000, 10001); diff --git a/test/sqls/marathonRegInfo/informixoltp__insert_test_data b/test/sqls/marathonRegInfo/informixoltp__insert_test_data new file mode 100755 index 000000000..0f8190117 --- /dev/null +++ b/test/sqls/marathonRegInfo/informixoltp__insert_test_data @@ -0,0 +1,61 @@ +INSERT INTO contest (contest_id, name, status, group_id) VALUES (10000, 'Marathon Match 90', 'A', -1); +INSERT INTO contest (contest_id, name, status, group_id) VALUES (10001, 'Marathon Match 91', 'A', -1); + +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2001, 10000, 'Round 2001', 'A', 13, 'Test Round 2001', 0, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2002, 1000, 'Test Round 2002', 'A', 13, 'Test Round 2002', 0, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2003, 1000, 'Test Round 2003', 'A', 13, 'Test Round 2003', 1, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2004, 1000, 'Test Round 2004', 'A', 13, 'Test Round 2004', 0, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2005, 1000, 'Test Round 2005', 'A', 13, 'Test Round 2005', 1, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2006, 10001, 'Test Round 2006', 'A', 13, 'Test Round 2006', 0, 10); +INSERT INTO round(round_id, contest_id, name, status, round_type_id, short_name, invitational, registration_limit) VALUES(2007, 10001, 'Test Round 2007', 'A', 13, 'Test Round 2007', 0, 10); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2001, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2001, 2, current - 50 units day, current + 30 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2002, 1, current - 50 units day, current - 1 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2002, 2, current - 50 units day, current - 1 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2003, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2003, 2, current - 50 units day, current + 30 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2004, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2004, 2, current - 50 units day, current + 60 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2005, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2005, 2, current - 50 units day, current + 60 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2006, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2006, 2, current - 50 units day, current + 30 units day, 'P'); + +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2007, 1, current - 50 units day, current + 30 units day, 'P'); +INSERT INTO round_segment(round_id, segment_id, start_time, end_time, status) VALUES(2007, 2, current - 50 units day, current + 30 units day, 'P'); + +INSERT INTO round_component(round_id, component_id, division_id) VALUES(2004, 2021, 1); +INSERT INTO round_component(round_id, component_id, division_id) VALUES(2005, 2021, 1); + +INSERT INTO invite_list(coder_id, round_id) VALUES(124772, 2005); + +INSERT INTO round_event(round_id, event_id, event_name, registration_url) VALUES(2001, 2001, 'Test Event 2001', 'https://foo.com'); +INSERT INTO round_event(round_id, event_id, event_name, registration_url) VALUES(2006, 2001, 'Test Event 2006', 'https://foo.com'); +INSERT INTO round_event(round_id, event_id, event_name, registration_url) VALUES(2007, 2001, 'Test Event 2007', 'https://foo.com'); + +INSERT INTO round_registration(round_id, coder_id, timestamp, eligible, team_id) +VALUES(2001, 124766, current, 1, null); +INSERT INTO team_coder_xref(team_id, coder_id, create_date) VALUES(36024, 132456, current); + +INSERT INTO round_terms(round_id) VALUES(2001); +INSERT INTO round_terms(round_id) VALUES(2006); + +INSERT INTO question(question_id, question_text, status_id, question_type_id, question_style_id) VALUES (1000, 'question 1000', 1, 2, 2); +INSERT INTO question(question_id, question_text, status_id, question_type_id, question_style_id) VALUES (1001, 'question 1001', 1, 3, 4); +INSERT INTO question(question_id, question_text, status_id, question_type_id, question_style_id) VALUES (1002, 'question 1002', 1, 2, 1); + +INSERT INTO round_question(round_id, question_id) VALUES (2001, 1000); +INSERT INTO round_question(round_id, question_id) VALUES (2001, 1001); +INSERT INTO round_question(round_id, question_id) VALUES (2001, 1002); + +INSERT INTO answer(answer_id, question_id, answer_text, sort_order, correct) VALUES (1001, 1000, 'answer text 2', 2, 0); +INSERT INTO answer(answer_id, question_id, answer_text, sort_order, correct) VALUES (1002, 1000, 'answer text 1', 1, 1); +INSERT INTO answer(answer_id, question_id, answer_text, sort_order, correct) VALUES (1003, 1000, 'answer text 3', 3, NULL); +INSERT INTO answer(answer_id, question_id, answer_text, sort_order, correct) VALUES (1004, 1002, 'answer text 4', NULL, 0); +INSERT INTO answer(answer_id, question_id, answer_text, sort_order, correct) VALUES (1005, 1002, 'answer text 5', 1, 1); diff --git a/test/test.marathonRegInfo.js b/test/test.marathonRegInfo.js new file mode 100755 index 000000000..1af1da449 --- /dev/null +++ b/test/test.marathonRegInfo.js @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2014 TopCoder Inc., All Rights Reserved. + * + * @version 1.0 + * @author TCSASSEMBLER + */ +'use strict'; +/*global describe, it, before, beforeEach, after, afterEach */ +/*jslint node: true, stupid: true, unparam: true */ + +/** + * Module dependencies. + */ +var request = require('supertest'); +var assert = require('chai').assert; +var async = require('async'); + +var testHelper = require('./helpers/testHelper'); +var SQL_DIR = __dirname + '/sqls/marathonRegInfo/'; +var API_ENDPOINT = process.env.API_ENDPOINT || 'http://localhost:8080'; + +describe('Marathon Match Challenge Reg Info API', function () { + this.timeout(120000); // The api with testing remote db could be quit slow + + var heffan = testHelper.generateAuthHeader({ sub: 'ad|132456' }), + member1 = testHelper.generateAuthHeader({ sub: 'ad|132457' }), + member2 = testHelper.generateAuthHeader({ sub: 'ad|132458' }), + member3 = testHelper.generateAuthHeader({ sub: 'ad|124764' }), + member4 = testHelper.generateAuthHeader({ sub: 'ad|124772' }), + member5 = testHelper.generateAuthHeader({ sub: 'ad|124766' }), + forbiddenUser = testHelper.generateAuthHeader({ sub: 'ad|300001' }), + unActivatedUser = testHelper.generateAuthHeader({ sub: 'ad|300002' }), + notExistedUser = testHelper.generateAuthHeader({ sub: 'ad|1234567890' }), + iranUser = testHelper.generateAuthHeader({ sub: 'ad|300003' }); + + /** + * Clear database + * @param {Function} done the callback + */ + function clearDb(done) { + async.waterfall([ + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'informixoltp__clean', 'informixoltp', cb); + }, function (cb) { + testHelper.runSqlFile(SQL_DIR + 'common_oltp__clean', 'common_oltp', cb); + } + ], done); + } + + /** + * This function is run before all tests. + * Generate tests data. + * @param {Function} done the callback + */ + before(function (done) { + async.waterfall([ + clearDb, + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'common_oltp__insert_test_data', 'common_oltp', cb); + }, + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'informixoltp__insert_test_data', 'informixoltp', cb); + }, + function (cb) { + testHelper.updateTextColumn('update round_terms set terms_content = ? where round_id IN (2001, 2006)', 'informixoltp', [{type: 'text', value : 'Marathon Match terms content'}], cb); + } + ], done); + }); + + /** + * This function is run after all tests. + * Clean up all data. + * @param {Function} done the callback + */ + after(function (done) { + clearDb(done); + }); + + /** + * Create a http request. + * @param {String} roundId - the request roundId. + * @param {Object} authHeader - the auth header for request. + */ + function createRequest(roundId, authHeader) { + var req = request(API_ENDPOINT) + .get('/v2/data/marathon/challenges/' + roundId + '/regInfo') + .set('Accept', 'application/json'); + if (authHeader) { + req.set('Authorization', authHeader); + } + return req.expect('Content-Type', /json/); + } + + /** + * Helper method for validating marathon match register information + * + * @param {String} roundId - the request roundId. + * @param {Object} authHeader - the auth header for request. + * @param {String} expectFile - the expect file path + * @param {Function} done - the callback function + */ + function validateResult(roundId, authHeader, expectFile, done) { + createRequest(roundId, authHeader).expect(200).end(function (err, res) { + if (err) { + done(err); + return; + } + var expected = require(expectFile); + delete res.body.serverInformation; + delete res.body.requesterInformation; + assert.deepEqual(res.body, expected, "Invalid response"); + done(); + }); + } + + /** + * Assert error request. + * + * @param {String} roundId - the request roundId. + * @param {Object} authHeader - the auth header for request. + * @param {Number} statusCode - the expected status code + * @param {String} errorDetail - the error detail. + * @param {Function} done the callback function + */ + function assertError(roundId, authHeader, statusCode, errorDetail, done) { + createRequest(roundId, authHeader).expect(statusCode).end(function (err, res) { + if (err) { + done(err); + return; + } + assert.equal(res.body.error.details, errorDetail, "Invalid error detail"); + done(); + }); + } + + /** + * Test when authorization is missing. + */ + it('should return authorized error. The authorization is missing.', function (done) { + assertError('2001', null, 401, 'Authorization information needed or incorrect.', done); + }); + + /** + * Test when roundId is not number. + */ + it('should return bad request. The roundId is not number.', function (done) { + assertError('abc', member1, 400, 'roundId should be number.', done); + }); + + /** + * Test when roundId is not integer. + */ + it('should return bad request. The roundId is not integer.', function (done) { + assertError('1.234', member1, 400, 'roundId should be Integer.', done); + }); + + /** + * Test when roundId is not positive. + */ + it('should return bad request. The roundId is not positive.', function (done) { + assertError('-1', member1, 400, 'roundId should be positive.', done); + }); + + /** + * Test when roundId is zero. + */ + it('should return bad request. The roundId is zero.', function (done) { + assertError('0', member1, 400, 'roundId should be positive.', done); + }); + + /** + * Test when roundId is too big. + */ + it('should return bad request. The roundId is too big.', function (done) { + assertError('2147483648', member1, 400, 'roundId should be less or equal to 2147483647.', done); + }); + + /** + * Test when user is forbidden to access register endpoint. + */ + it('should return bad request. The user don\'t have the access to register endpoint.', function (done) { + assertError('2001', forbiddenUser, 403, 'The user is forbidden to access this endpoint.', done); + }); + + /** + * Test when user is not activated. + */ + it('should return bad request. The user is not activated.', function (done) { + assertError('2001', unActivatedUser, 400, 'You are not eligible to participate in this competition.', done); + }); + + /** + * Test when user is not existed. + */ + it('should return bad request. The user is not existed.', function (done) { + assertError('2001', notExistedUser, 500, 'user not found with id=1234567890', done); + }); + + /** + * Test when round is not existed. + */ + it('should return bad request. The challenge round is not existed.', function (done) { + assertError('3001', member1, 400, 'Round doesn\'t exist 3001.', done); + }); + + /** + * Test when round has a event but the user didn't register it. + */ + it('should return bad request. The event not registered.', function (done) { + assertError('2001', member2, 400, 'In order to participate in this competition, you must register for ' + + 'Test Event 2001. Registration is available: here. ' + + 'Please register at the provided URL first and then repeat registration at Marathon Match Active Contests page.', done); + }); + + /** + * Test when registration of round event is not eligible. + */ + it('should return bad request. The registration of event is not eligible.', function (done) { + assertError('2001', member3, 400, 'You are not eligible to participate in this competition.', done); + }); + + /** + * Test when round registration is closed. + */ + it('should return bad request. The round registration is closed.', function (done) { + assertError('2002', member1, 400, 'Registration is not currently open.', done); + }); + + /** + * Test when user is in a invalid country. + */ + it('should return bad request. The user is in invalid country.', function (done) { + assertError('2001', iranUser, 400, 'You are not eligible to participate in this competition. Please contact support@topcoder.com if you have any questions.', done); + }); + + /** + * Test when round is required invitation and the user don't have one. + */ + it('should return bad request. The user is not invited to this challenge.', function (done) { + assertError('2003', member1, 400, 'Sorry, this round is by invitation only.', done); + }); + + /** + * Test when the round is parallel round. + */ + it('should return bad request. The round is parallel round.', function (done) { + assertError('2004', member4, 400, 'Sorry, you can not register for this round, you must compete in the version of this round that you were invited to.', done); + }); + + /** + * Test when the round term doesn't exist. + */ + it('should return not found. The round term doesn\'t exist.', function (done) { + assertError('2007', heffan, 404, 'Could not find specified round terms.', done); + }); + + /** + * Test when the round meet registration limit. + */ + it('should return bad request. The round has no empty positions for registrants.', function (done) { + async.waterfall([ + function (cb) { + testHelper.runSqlQuery('UPDATE round SET registration_limit = 0 WHERE round_id = 2001', 'informixoltp', cb); + }, + function (cb) { + assertError('2001', heffan, 400, 'There are no more spots available for the round.', cb); + }, + function (cb) { + testHelper.runSqlQuery('UPDATE round SET registration_limit = 10 WHERE round_id = 2001', 'informixoltp', cb); + } + ], done); + }); + + /** + * Get marathon match register information for round 2001. + * Expect success results. + */ + it('should return success results for round 2001', function (done) { + validateResult('2001', heffan, './test_files/expected_marathon_reg_info.json', done); + }); + + /** + * Get marathon match register information for round 2001. + * user twight already register round 2001, expect success results. + */ + it('should return success results for round 2001 when user already register', function (done) { + validateResult('2001', member5, './test_files/expected_marathon_reg_info.json', done); + }); + + /** + * Get marathon match register information for round 2006. + * Expect success results with empty questions. + */ + it('should return success results with empty questions for round 2006', function (done) { + validateResult('2006', heffan, './test_files/expected_marathon_reg_info_empty_questions.json', done); + }); +}); diff --git a/test/test_files/expected_marathon_reg_info.json b/test/test_files/expected_marathon_reg_info.json new file mode 100755 index 000000000..1a1961473 --- /dev/null +++ b/test/test_files/expected_marathon_reg_info.json @@ -0,0 +1,62 @@ +{ + "term": { + "contestName": "Marathon Match 90", + "roundName": "Round 2001", + "termsContent": "Marathon Match terms content" + }, + "questions": [ + { + "id": 1000, + "style": "Multiple Choice", + "type": "Elligble", + "text": "question 1000", + "answers": [ + { + "id": 1002, + "text": "answer text 1", + "sortOrder": 1, + "correct": true + }, + { + "id": 1001, + "text": "answer text 2", + "sortOrder": 2, + "correct": false + }, + { + "id": 1003, + "text": "answer text 3", + "sortOrder": 3, + "correct": false + } + ] + }, + { + "id": 1001, + "style": "Short Answer", + "type": "Personal", + "text": "question 1001", + "answers": [] + }, + { + "id": 1002, + "style": "Single Choice", + "type": "Elligble", + "text": "question 1002", + "answers": [ + { + "id": 1004, + "text": "answer text 4", + "sortOrder": -1, + "correct": false + }, + { + "id": 1005, + "text": "answer text 5", + "sortOrder": 1, + "correct": true + } + ] + } + ] +} \ No newline at end of file diff --git a/test/test_files/expected_marathon_reg_info_empty_questions.json b/test/test_files/expected_marathon_reg_info_empty_questions.json new file mode 100755 index 000000000..2191f8339 --- /dev/null +++ b/test/test_files/expected_marathon_reg_info_empty_questions.json @@ -0,0 +1,8 @@ +{ + "term": { + "contestName": "Marathon Match 91", + "roundName": "Test Round 2006", + "termsContent": "Marathon Match terms content" + }, + "questions": [] +} \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy