blob: 494993ca753dc79aa292c6753aaabd7dcc36f35c [file] [log] [blame]
# Copyright 2014 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for proposal logic."""
import mock
import unittest
from google.appengine.ext import ndb
from melange.models import organization as org_model
from summerofcode.logic import proposal as proposal_logic
from summerofcode.models import proposal as proposal_model
from summerofcode.views.helper import urls
from tests import org_utils
from tests import profile_utils
from tests import program_utils
from tests import test_utils
from tests import timeline_utils
from tests.utils import proposal_utils
_TEST_PROPOSAL_PROPERTIES = {
'title': 'Test Title',
'abstract': 'Test Abstract',
'content': 'Test Content',
'additional_info': 'http://www.test.additional.info.com'
}
_OTHER_PROPOSAL_PROPERTIES = {
'title': u'Other Title',
'abstract': u'Other Abstract',
'content': u'Other Content',
'additional_info': u'http://www.other.additional.info.com'
}
_TEST_COMMENT_CONTENT = 'Test comment content'
class CreateProposalTest(unittest.TestCase):
"""Unit tests for createProposal function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.student = profile_utils.seedSOCStudent(self.program)
self.org = org_utils.seedSOCOrganization(self.program.key())
def testProposalCanBeCreated(self):
"""Tests that proposal is created successfully if it is allowed."""
proposal_properties = _TEST_PROPOSAL_PROPERTIES.copy()
proposal_properties['program'] = ndb.Key.from_old_key(self.program.key())
result = proposal_logic.createProposal(
self.student.key, self.org, self.program, proposal_properties)
self.assertTrue(result)
proposal = result.extra
self.assertDictContainsSubset(_TEST_PROPOSAL_PROPERTIES, proposal.to_dict())
self.assertEqual(proposal.key.parent(), self.student.key)
self.assertEqual(proposal.program.to_old_key(), self.program.key())
self.assertEqual(proposal.organization, self.org.key)
self.assertListEqual(proposal.scores, [])
self.assertListEqual(proposal.possible_mentors, [])
@mock.patch.object(
proposal_logic, 'isProposalLimitReached', return_value=True)
def testProposalCannotBeCreated(self, mock_func):
"""Tests that proposal is not created when it is not allowed."""
proposal_properties = _TEST_PROPOSAL_PROPERTIES.copy()
proposal_properties['program'] = ndb.Key.from_old_key(self.program.key())
result = proposal_logic.createProposal(
self.student.key, self.org, self.program, proposal_properties)
self.assertFalse(result)
def testInvalidData(self):
"""Tests that proposal entity is not created if data is not valid."""
proposal_properties = _TEST_PROPOSAL_PROPERTIES.copy()
proposal_properties['program'] = ndb.Key.from_old_key(self.program.key())
proposal_properties['organization'] = self.org.key
# invalid additional info
invalid_properties = proposal_properties.copy()
invalid_properties['additional_info'] = 'not-a-link'
result = proposal_logic.createProposal(
self.student.key, self.org, self.program, invalid_properties)
self.assertFalse(result)
class UpdateProposalTest(unittest.TestCase):
"""Unit tests for updateProposal function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.student = profile_utils.seedSOCStudent(self.program)
self.org = org_utils.seedSOCOrganization(self.program.key())
self.proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key)
def testProposaCanlUpdated(self):
"""Tests that proposal is updated properly."""
# set expected properties, after the update
expected_properties = self.proposal.to_dict()
expected_properties.update(_OTHER_PROPOSAL_PROPERTIES)
del expected_properties['modified_on']
result = proposal_logic.updateProposal(
self.proposal.key, _OTHER_PROPOSAL_PROPERTIES)
self.assertTrue(result)
# check that updated properties are updated
proposal = self.proposal.key.get()
actual_properties = proposal.to_dict()
del actual_properties['modified_on']
self.assertDictEqual(expected_properties, actual_properties)
def testIllegalProperty(self):
"""Tests that an error is raised when an illegal property is updated."""
# check for trying to change program
other_program = program_utils.seedGSoCProgram()
proposal_properties = _OTHER_PROPOSAL_PROPERTIES.copy()
proposal_properties['program'] = ndb.Key.from_old_key(other_program.key())
with self.assertRaises(ValueError):
proposal_logic.updateProposal(
self.proposal.key, proposal_properties)
# check for trying to change organization
other_org = org_utils.seedSOCOrganization(self.program.key())
proposal_properties = _OTHER_PROPOSAL_PROPERTIES.copy()
proposal_properties['organization'] = other_org.key
with self.assertRaises(ValueError):
proposal_logic.updateProposal(
self.proposal.key, proposal_properties)
# check for trying to change status
other_status = org_model.Status.ACCEPTED
proposal_properties = _OTHER_PROPOSAL_PROPERTIES.copy()
proposal_properties['status'] = other_status
with self.assertRaises(ValueError):
proposal_logic.updateProposal(
self.proposal.key, proposal_properties)
_NUMBER_OF_PROPOSALS_FOR_ORG_ONE = 2
_NUMBER_OF_PROPOSALS_FOR_ORG_TWO = 3
class QueryProposalsForStudentTest(unittest.TestCase):
"""Unit tests for queryProposalsForStudent function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org_one = org_utils.seedSOCOrganization(self.program.key())
self.org_two = org_utils.seedSOCOrganization(self.program.key())
def testFetchedProposals(self):
"""Tests that only correct proposals are fetched by the returned query."""
expected_keys = set()
# seed a student and a couple of proposals for both orgs
student = profile_utils.seedNDBStudent(self.program)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_ONE):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_one.key).key)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_TWO):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_two.key).key)
# seed another student with proposals
other_student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
other_student.key, self.program.key(), org_key=self.org_one.key)
proposal_utils.seedNDBProposal(
other_student.key, self.program.key(), org_key=self.org_two.key)
query = proposal_logic.queryProposalsForStudent(student.key)
# check that only expected proposals are fetched
actual_keys = query.fetch(1000, keys_only=True)
self.assertSetEqual(set(actual_keys), expected_keys)
class QueryProposalsForOrganizationMemberTest(unittest.TestCase):
"""Unit tests for queryProposalsForOrganizationMember function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org_one = org_utils.seedSOCOrganization(self.program.key())
self.org_two = org_utils.seedSOCOrganization(self.program.key())
def testFetchedProposals(self):
"""Tests that only correct proposals are fetched by the returned query."""
expected_keys = set()
# seed a few proposals for both organizations
student = profile_utils.seedNDBStudent(self.program)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_ONE):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_one.key).key)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_TWO):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_two.key).key)
# seed another organization and a proposal
other_org = org_utils.seedSOCOrganization(self.program.key())
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=other_org.key)
# seed a mentor for organization one and two but not for the other org
profile = profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org_one.key, self.org_two.key])
query = proposal_logic.queryProposalsForOrganizationMember(profile)
# check that only expected proposals are fetched
actual_keys = query.fetch(1000, keys_only=True)
self.assertSetEqual(set(actual_keys), expected_keys)
class QueryProposalsForProgramHostTest(unittest.TestCase):
"""Unit tests for queryProposalsForProgramHost function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org_one = org_utils.seedSOCOrganization(self.program.key())
self.org_two = org_utils.seedSOCOrganization(self.program.key())
def testFetchedProposals(self):
"""Tests that only correct proposals are fetched by the returned query."""
expected_keys = set()
# seed a student and a couple of proposals for both orgs
student = profile_utils.seedNDBStudent(self.program)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_ONE):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_one.key).key)
for _ in range(_NUMBER_OF_PROPOSALS_FOR_ORG_TWO):
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org_two.key).key)
query = proposal_logic.queryProposalsForProgramHost(self.program.key())
# check that all proposals are fetched
actual_keys = query.fetch(1000, keys_only=True)
self.assertSetEqual(set(actual_keys), expected_keys)
_NUMBER_OF_SCORES = 3
class GetScoreForProfileTest(unittest.TestCase):
"""Unit tests for getScoreForProfile function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
student = profile_utils.seedSOCStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key())
self.profile = profile_utils.seedNDBProfile(self.program.key())
def testNoScoreExists(self):
"""Tests that None is returned if no score exists."""
score = proposal_logic.getScoreForProfile(self.proposal, self.profile.key)
self.assertIsNone(score)
def testScoreExists(self):
"""Tests that the score is returned if exists."""
# seed a couple of other scores
for _ in range(_NUMBER_OF_SCORES):
profile = profile_utils.seedNDBProfile(self.program.key())
self.proposal.scores.append(
proposal_model.Score(author=profile.key, value=1))
# seed a score for the profile
self.proposal.scores.append(
proposal_model.Score(author=self.profile.key, value=2))
# seed a couple of more scores
for _ in range(_NUMBER_OF_SCORES):
profile = profile_utils.seedNDBProfile(self.program.key())
self.proposal.scores.append(
proposal_model.Score(author=profile.key, value=1))
score = proposal_logic.getScoreForProfile(self.proposal, self.profile.key)
self.assertIsNotNone(score)
self.assertEqual(score.author, self.profile.key)
self.assertEqual(score.value, 2)
_TEST_NUMBER_OF_SCORES = 2
_TEST_SCORE_VALUE = 3
_TEST_MAX_SCORE = 2 * _TEST_SCORE_VALUE
_TEST_UPDATED_SCORE_VALUE = 1
class SetScoreForProfileTest(unittest.TestCase):
"""Unit tests for setScoreForProfile function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org = org_utils.seedSOCOrganization(
self.program.key(), max_score=_TEST_MAX_SCORE)
student = profile_utils.seedSOCStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
self.profile = profile_utils.seedNDBProfile(self.program.key())
def testSetScore(self):
"""Tests that a score is set properly."""
# set a couple of other scores
for _ in range(_TEST_NUMBER_OF_SCORES):
profile = profile_utils.seedNDBProfile(self.program.key())
self.proposal.scores.append(
proposal_model.Score(author=profile.key, value=_TEST_SCORE_VALUE))
self.proposal.put()
# assign a new score
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, _TEST_SCORE_VALUE, self.org)
self.assertTrue(result)
proposal = self.proposal.key.get()
scores = [
score for score in proposal.scores if score.author == self.profile.key]
self.assertEqual(len(scores), 1)
self.assertEqual(scores[0].value, _TEST_SCORE_VALUE)
def testUpdateScore(self):
"""Tests that a score is set properly."""
# set the initial score
self.proposal.scores.append(
proposal_model.Score(author=self.profile.key, value=_TEST_SCORE_VALUE))
# set a couple of other scores
for _ in range(_TEST_NUMBER_OF_SCORES):
profile = profile_utils.seedNDBProfile(self.program.key())
self.proposal.scores.append(
proposal_model.Score(author=profile.key, value=_TEST_SCORE_VALUE))
self.proposal.put()
# update the score
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key,
_TEST_UPDATED_SCORE_VALUE, self.org)
self.assertTrue(result)
proposal = self.proposal.key.get()
scores = [
score for score in proposal.scores if score.author == self.profile.key]
self.assertEqual(len(scores), 1)
self.assertEqual(scores[0].value, _TEST_UPDATED_SCORE_VALUE)
def testCleanScore(self):
"""Tests that a score is cleaned properly."""
# set a couple of other scores
for _ in range(_TEST_NUMBER_OF_SCORES):
profile = profile_utils.seedNDBProfile(self.program.key())
self.proposal.scores.append(
proposal_model.Score(author=profile.key, value=_TEST_SCORE_VALUE))
self.proposal.put()
# clean the score initially
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, None, self.org)
self.assertTrue(result)
# check that no scores have been removed
proposal = self.proposal.key.get()
self.assertEqual(len(proposal.scores), _TEST_NUMBER_OF_SCORES)
# set the initial score
self.proposal.scores.append(
proposal_model.Score(author=self.profile.key, value=_TEST_SCORE_VALUE))
self.proposal.put()
# clean the score
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, None, self.org)
self.assertTrue(result)
proposal = self.proposal.key.get()
scores = [
score for score in proposal.scores if score.author == self.profile.key]
self.assertEqual(len(scores), 0)
def testScoringNotEnabled(self):
"""Tests that no score is set if scoring is not enabled for organization."""
self.org.scoring_enabled = False
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, _TEST_SCORE_VALUE, self.org)
self.assertFalse(result)
def testScoreValueRange(self):
"""Tests that no score is set if it has invalid value."""
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, -1, self.org)
self.assertFalse(result)
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, 0, self.org)
self.assertFalse(result)
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, 1, self.org)
self.assertTrue(result)
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, _TEST_MAX_SCORE, self.org)
self.assertTrue(result)
result = proposal_logic.setScoreForProfile(
self.proposal.key, self.profile.key, _TEST_MAX_SCORE + 1, self.org)
self.assertFalse(result)
_NUMBER_OF_PEOPLE_TO_NOTIFY = 2
class CreateCommentTest(test_utils.DjangoTestCase):
"""Unit tests for the createComment function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.init()
self.program = program_utils.seedGSoCProgram()
student = profile_utils.seedSOCStudent(self.program)
self.org = org_utils.seedSOCOrganization(self.program.key())
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
self.profile = profile_utils.seedNDBProfile(self.program.key())
def testPrivateCommentCreated(self):
"""Tests that a private comment is created properly."""
comment = proposal_logic.createComment(
self.proposal, _TEST_COMMENT_CONTENT, self.profile, True)
# check that the comment is persisted in datastore
comment = comment.key.get()
self.assertIsNotNone(comment)
# check properties of the comment
self.assertEqual(comment.key.parent(), self.proposal.key)
self.assertEqual(comment.content, _TEST_COMMENT_CONTENT)
self.assertEqual(comment.author, self.profile.key)
self.assertTrue(comment.is_private)
def testPublicCommentCreated(self):
"""Tests that a public comment is created properly."""
comment = proposal_logic.createComment(
self.proposal, _TEST_COMMENT_CONTENT, self.profile, False)
# check that the comment is persisted in datastore
comment = comment.key.get()
self.assertIsNotNone(comment)
# check properties of the comment
self.assertEqual(comment.key.parent(), self.proposal.key)
self.assertEqual(comment.content, _TEST_COMMENT_CONTENT)
self.assertEqual(comment.author, self.profile.key)
self.assertFalse(comment.is_private)
def testNotificationEmailsSent(self):
"""Tests that notification emails are sent properly."""
site = program_utils.seedSite()
# seed a few profiles to which notifications are sent
profiles_to_notify = []
for _ in range(_NUMBER_OF_PEOPLE_TO_NOTIFY):
profiles_to_notify.append(
profile_utils.seedNDBProfile(self.program.key()))
proposal_logic.createComment(
self.proposal, _TEST_COMMENT_CONTENT, self.profile, False,
profiles_to_notify=profiles_to_notify, program=self.program,
organization=self.org, site=site, url_names=urls.UrlNames)
# check that notifications have been sent
subject_context = {
'org_name': self.org.name,
'visibility': 'public',
'proposal_title': self.proposal.title,
}
subject = proposal_logic._NEW_COMMENT_MAIL_SUBJECT % subject_context
self.assertEmailSent(
bcc=', '.join(profile.contact.email for profile in profiles_to_notify),
subject=subject)
_NUMBER_OF_PUBLIC_COMMENTS = 3
_NUMBER_OF_PRIVATE_COMMENTS = 2
class GetCommentsTest(unittest.TestCase):
"""Unit tests for the getComments function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
student = profile_utils.seedSOCStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key())
# seed a few public comments
for _ in range(_NUMBER_OF_PUBLIC_COMMENTS):
proposal_utils.seedComment(self.proposal)
# seed a few private comments
for _ in range(_NUMBER_OF_PRIVATE_COMMENTS):
proposal_utils.seedComment(self.proposal, is_private=True)
def testGetPublicCommentsOnly(self):
"""Tests that only public comments are be returned."""
comments = proposal_logic.getComments(
self.proposal.key, exclude_private=True)
self.assertEqual(len(comments), _NUMBER_OF_PUBLIC_COMMENTS)
for comment in comments:
self.assertFalse(comment.is_private)
def testGetAllComments(self):
"""Tests that all comments are be returned."""
comments = proposal_logic.getComments(self.proposal.key)
self.assertEqual(
len(comments), _NUMBER_OF_PUBLIC_COMMENTS + _NUMBER_OF_PRIVATE_COMMENTS)
_TEST_PROPOSAL_LIMIT = 3
class IsProposalLimitReachedTest(unittest.TestCase):
"""Unit tests for isProposalLimitReached class."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
sponsor = program_utils.seedSponsor()
self.program = program_utils.seedGSoCProgram(
sponsor_key=sponsor.key(), apps_tasks_limit=_TEST_PROPOSAL_LIMIT)
user = profile_utils.seedNDBUser()
profile_utils.loginNDB(user)
self.profile = profile_utils.seedNDBStudent(self.program, user=user)
def testLimitNotReached(self):
"""Tests that False is returned if limit is not reached."""
# seed limit - 1 proposals
for _ in range(_TEST_PROPOSAL_LIMIT - 1):
proposal_utils.seedNDBProposal(self.profile.key, self.program.key())
# seed also a withdrawn proposals
proposal_utils.seedNDBProposal(
self.profile.key, self.program.key(),
status=proposal_model.Status.WITHDRAWN)
self.assertFalse(proposal_logic.isProposalLimitReached(
self.profile, self.program))
def testLimitReached(self):
"""Tests that True is returned if limit is reached."""
for _ in range(_TEST_PROPOSAL_LIMIT):
proposal_utils.seedNDBProposal(self.profile.key, self.program.key())
self.assertTrue(proposal_logic.isProposalLimitReached(
self.profile, self.program))
_TEST_NUMBER_OF_RECIPIENTS = 3
class DispatchNewCommentEmailTest(test_utils.DjangoTestCase):
"""Unit tests for dispatchNewCommentEmail function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.init()
def testNewCommentEmailSent(self):
"""Tests that "New Comment" email is sent properly."""
site = program_utils.seedSite()
program = program_utils.seedProgram()
org = org_utils.seedOrganization(program.key())
# seed a proposal
student = profile_utils.seedNDBStudent(program)
proposal = proposal_utils.seedNDBProposal(
student.key, program.key(), org_key=org.key)
# seed a comment for the proposal
author = profile_utils.seedNDBProfile(program.key())
comment = proposal_utils.seedComment(
proposal, author_key=author.key, is_private=False)
# seed a few profiles to which emails are sent
profiles = []
for _ in range(_TEST_NUMBER_OF_RECIPIENTS):
profiles.append(profile_utils.seedNDBProfile(program.key()))
proposal_logic.dispatchNewCommentEmail(
profiles, comment, author, proposal, org, program, site,
'unused', 'unused')
subject_context = {
'org_name': org.name,
'visibility': 'public',
'proposal_title': proposal.title,
}
subject = proposal_logic._NEW_COMMENT_MAIL_SUBJECT % subject_context
self.assertEmailSent(
bcc=', '.join([profile.contact.email for profile in profiles]),
subject=subject)
class DispatchNewProposalEmailTest(test_utils.DjangoTestCase):
"""Unit tests for dispatchNewProposalEmail function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.init()
def testNewProposalEmailSent(self):
"""Tests that "New Proposal" email is sent properly."""
site = program_utils.seedSite()
program = program_utils.seedProgram()
org = org_utils.seedOrganization(program.key())
# seed a proposal
student = profile_utils.seedNDBStudent(program)
proposal = proposal_utils.seedNDBProposal(
student.key, program.key(), org_key=org.key)
# seed a few profiles to which emails are sent
profiles = []
for _ in range(_TEST_NUMBER_OF_RECIPIENTS):
profiles.append(profile_utils.seedNDBProfile(program.key()))
proposal_logic.dispatchNewProposalEmail(
profiles, proposal, org, student, program, site, 'unused', 'unused')
subject_context = {
'org_name': org.name,
'student_name': student.public_name,
'proposal_title': proposal.title,
}
subject = proposal_logic._DEF_NEW_PROPOSAL_MAIL_SUBJECT % subject_context
self.assertEmailSent(
bcc=', '.join([profile.contact.email for profile in profiles]),
subject=subject)
_TEST_NUMBER_OF_POSSIBLE_MENTORS = 3
class AddPossibleMentorTest(unittest.TestCase):
"""Unit tests for addPossibleMentor class."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedProgram()
self.org = org_utils.seedOrganization(self.program.key())
# seed a proposal
student = profile_utils.seedNDBStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
def testPossibleMentorAdded(self):
"""Tests that a mentor is added as a possible mentor."""
profile = profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key])
proposal_logic.addPossibleMentor(self.proposal.key, profile.key)
# check that the possible mentor is added
proposal = self.proposal.key.get()
self.assertIn(profile.key, proposal.possible_mentors)
# try adding the same profile again
proposal_logic.addPossibleMentor(self.proposal.key, profile.key)
# check that the possible mentor is still added
proposal = self.proposal.key.get()
self.assertIn(profile.key, proposal.possible_mentors)
def testAddMorePossibleMentors(self):
"""Tests that all mentors are added as possible mentors."""
profile_keys = []
for _ in range(_TEST_NUMBER_OF_POSSIBLE_MENTORS):
profile_keys.append(profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key]).key)
for profile_key in profile_keys:
proposal_logic.addPossibleMentor(self.proposal.key, profile_key)
# check that all possible mentors are added
proposal = self.proposal.key.get()
for profile_key in profile_keys:
self.assertIn(profile_key, proposal.possible_mentors)
class RemovePossibleMentorTest(unittest.TestCase):
"""Unit tests for removePossibleMentor class."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedProgram()
self.org = org_utils.seedOrganization(self.program.key())
# seed a proposal
student = profile_utils.seedNDBStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
def testPossibleMentorRemoved(self):
"""Tests that a possible mentor is removed."""
# add a few possible mentors
profile_keys = []
for _ in range(_TEST_NUMBER_OF_POSSIBLE_MENTORS):
profile_keys.append(profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key]).key)
# add one more possible mentor
profile = profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key])
profile_keys.append(profile.key)
self.proposal.possible_mentors = profile_keys
self.proposal.put()
# remove the possible mentor
proposal_logic.removePossibleMentor(self.proposal.key, profile.key)
# check that the mentor is removed
proposal = self.proposal.key.get()
self.assertNotIn(profile.key, proposal.possible_mentors)
# check that the function does not fail on subsequent removes
proposal_logic.removePossibleMentor(self.proposal.key, profile.key)
class WithdrawProposalTest(unittest.TestCase):
"""Unit tests for withdrawProposal function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedProgram()
self.org = org_utils.seedOrganization(self.program.key())
self.student = profile_utils.seedNDBStudent(self.program)
def testPendingProposalWithdrawn(self):
"""Tests that the proposal is withdrawn correctly."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key)
result = proposal_logic.withdrawProposal(proposal.key)
self.assertTrue(result)
# check that the proposal is withdrawn
proposal = proposal.key.get()
self.assertEqual(proposal.status, proposal_model.Status.WITHDRAWN)
# check that the number of proposals is decreased
student = self.student.key.get()
self.assertEqual(
student.student_data.number_of_proposals,
self.student.student_data.number_of_proposals - 1)
def testWithdrawnProposalWithdrawn(self):
"""Tests that the proposal is withdrawn correctly."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.WITHDRAWN)
result = proposal_logic.withdrawProposal(proposal.key)
self.assertTrue(result)
# check that the proposal is still withdrawn
proposal = proposal.key.get()
self.assertEqual(proposal.status, proposal_model.Status.WITHDRAWN)
# check that the number of proposals is the same
student = self.student.key.get()
self.assertEqual(
student.student_data.number_of_proposals,
self.student.student_data.number_of_proposals)
def testAcceptedProposalNotWithdrawn(self):
"""Tests that accepted proposal is not withdrawn."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.ACCEPTED)
result = proposal_logic.withdrawProposal(proposal.key)
self.assertFalse(result)
self.assertEqual(result.extra, proposal_logic.ILLEGAL_PROPOSAL_STATE)
def testRejectedProposalNotWithdrawn(self):
"""Tests that rejected proposal is not withdrawn."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.REJECTED)
result = proposal_logic.withdrawProposal(proposal.key)
self.assertFalse(result)
self.assertEqual(result.extra, proposal_logic.ILLEGAL_PROPOSAL_STATE)
class ResubmitProposalTest(unittest.TestCase):
"""Unit tests for resubmitProposal function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org = org_utils.seedOrganization(self.program.key())
self.student = profile_utils.seedNDBStudent(self.program)
def testPendingProposalResubmitted(self):
"""Tests that the proposal is resubmitted correctly."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key)
result = proposal_logic.resubmitProposal(proposal.key, self.program)
self.assertTrue(result)
# check that the proposal is still pending
proposal = proposal.key.get()
self.assertEqual(proposal.status, proposal_model.Status.PENDING)
# check that the number of proposals is the same
student = self.student.key.get()
self.assertEqual(
student.student_data.number_of_proposals,
self.student.student_data.number_of_proposals)
def testWithdrawnProposalResubmitted(self):
"""Tests that the withdrawn proposal is resubmitted correctly."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.WITHDRAWN)
result = proposal_logic.resubmitProposal(proposal.key, self.program)
self.assertTrue(result)
# check that the proposal is pending
proposal = proposal.key.get()
self.assertEqual(proposal.status, proposal_model.Status.PENDING)
# check that the number of proposals is increased
student = self.student.key.get()
self.assertEqual(
student.student_data.number_of_proposals,
self.student.student_data.number_of_proposals + 1)
def testAcceptedProposalNotResubmitted(self):
"""Tests that accepted proposal is not resubmitted."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.ACCEPTED)
result = proposal_logic.resubmitProposal(proposal.key, self.program)
self.assertFalse(result)
self.assertEqual(result.extra, proposal_logic.ILLEGAL_PROPOSAL_STATE)
def testRejectedProposalNotResubmitted(self):
"""Tests that rejected proposal is not resubmitted."""
proposal = proposal_utils.seedNDBProposal(
self.student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.REJECTED)
result = proposal_logic.resubmitProposal(proposal.key, self.program)
self.assertFalse(result)
self.assertEqual(result.extra, proposal_logic.ILLEGAL_PROPOSAL_STATE)
_TEST_NUMBER_OF_MENTORS = 3
class AssignMentorsTest(unittest.TestCase):
"""Unit tests for assignMentors function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org = org_utils.seedOrganization(self.program.key())
student = profile_utils.seedNDBStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
def testMentorsAssigned(self):
"""Tests that mentors are assigned properly."""
mentors = []
for _ in range(_TEST_NUMBER_OF_MENTORS):
mentors.append(
profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key]))
result = proposal_logic.assignMentors(self.proposal.key, mentors)
self.assertTrue(result)
# check that mentors are assigned
proposal = self.proposal.key.get()
self.assertSetEqual(
set(proposal.mentors),
set(mentor.key for mentor in mentors))
def testsMentorsCleared(self):
"""Tests that mentors are cleared properly for the proposal."""
mentor_keys = []
for _ in range(_TEST_NUMBER_OF_MENTORS):
mentor_keys.append(
profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key]).key)
self.proposal.mentors = mentor_keys
self.proposal.put()
result = proposal_logic.assignMentors(self.proposal.key, [])
self.assertTrue(result)
# check that mentors are assigned
proposal = self.proposal.key.get()
self.assertListEqual(proposal.mentors, [])
def testMentorsNotAssigned(self):
"""Tests that mentors are not assigned if it is not eligible."""
mentors = []
for _ in range(_TEST_NUMBER_OF_MENTORS):
mentors.append(
profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key]))
illegal = profile_utils.seedNDBProfile(self.program.key())
mentors.append(illegal)
result = proposal_logic.assignMentors(self.proposal.key, mentors)
self.assertFalse(result)
self.assertEqual(result.extra[0].key, illegal.key)
_TEST_PROPOSAL_FIELD_ONE_ID = 'test field'
_TEST_PROPOSAL_FIELD_ONE_VALUE = 'test value'
_TEST_PROPOSAL_FIELD_ONE_OTHER_VALUE = 'test other value'
_TEST_PROPOSAL_FIELD_TWO_ID = 'other field'
_TEST_PROPOSAL_FIELD_TWO_VALUE = 'other value'
_TEST_NON_EXISTING_FIELD_ID = 'non-existing field'
_TEST_NON_EXISTING_FIELD_VALUE = 'unused value'
_TEST_EXTRA_DATA = {
_TEST_PROPOSAL_FIELD_ONE_ID: _TEST_PROPOSAL_FIELD_ONE_VALUE,
_TEST_PROPOSAL_FIELD_TWO_ID: _TEST_PROPOSAL_FIELD_TWO_VALUE,
_TEST_NON_EXISTING_FIELD_ID: _TEST_NON_EXISTING_FIELD_VALUE,
}
class SetExtraDataTest(unittest.TestCase):
"""Unit tests for setExtraData function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org = org_utils.seedOrganization(
self.program.key(), extra_fields=[
_TEST_PROPOSAL_FIELD_ONE_ID, _TEST_PROPOSAL_FIELD_TWO_ID])
student = profile_utils.seedNDBStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key)
def testExtraDataSet(self):
"""Tests that extra data is set correctly."""
proposal_logic.setExtraData(self.proposal.key, self.org, _TEST_EXTRA_DATA)
proposal = self.proposal.key.get()
# check that fields are set
self.assertEqual(
proposal.extra_data[_TEST_PROPOSAL_FIELD_ONE_ID],
_TEST_PROPOSAL_FIELD_ONE_VALUE)
self.assertEqual(
proposal.extra_data[_TEST_PROPOSAL_FIELD_TWO_ID],
_TEST_PROPOSAL_FIELD_TWO_VALUE)
# check that the non-existing field is not set
self.assertNotIn(_TEST_NON_EXISTING_FIELD_ID, proposal.extra_data)
def testExtraDataUpdated(self):
"""Tests that extra data is updated correctly if already exists."""
self.proposal.extra_data = {
_TEST_PROPOSAL_FIELD_ONE_ID: _TEST_PROPOSAL_FIELD_ONE_OTHER_VALUE
}
self.proposal.put()
proposal_logic.setExtraData(self.proposal.key, self.org, _TEST_EXTRA_DATA)
proposal = self.proposal.key.get()
# check that the field is updated
self.assertEqual(
proposal.extra_data[_TEST_PROPOSAL_FIELD_ONE_ID],
_TEST_PROPOSAL_FIELD_ONE_VALUE)
class CanStudentUpdateProposalTest(unittest.TestCase):
"""Unit tests for canStudentUpdateProposal function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
student = profile_utils.seedNDBStudent(self.program)
self.proposal = proposal_utils.seedNDBProposal(
student.key, self.program.key())
def testProposalCanBeUpdatedDuringStudentSignup(self):
"""Tests that the proposal can be updated during the student sign-up."""
self.program.timeline.student_signup_start = timeline_utils.past()
self.program.timeline.student_signup_end = timeline_utils.future()
result = proposal_logic.canStudentUpdateProposal(
self.proposal, self.program.timeline)
self.assertTrue(result)
def testProposalCanBeUpdatedAfterDeadlineIfAllowed(self):
"""Tests that the proposal can be updated if allowed after signup."""
self.program.timeline.student_signup_start = timeline_utils.past(delta=10)
self.program.timeline.student_signup_end = timeline_utils.past(delta=5)
self.proposal.is_editable_post_deadline = True
result = proposal_logic.canStudentUpdateProposal(
self.proposal, self.program.timeline)
self.assertTrue(result)
def testProposalCannotBeUpdatedAfterDeadlineIfNotAllowed(self):
"""Tests that the proposal cannot be updated if not allowed after signup."""
self.program.timeline.student_signup_start = timeline_utils.past(delta=10)
self.program.timeline.student_signup_end = timeline_utils.past(delta=5)
self.proposal.is_editable_post_deadline = False
result = proposal_logic.canStudentUpdateProposal(
self.proposal, self.program.timeline)
self.assertFalse(result)
_TEST_SLOT_ALLOCATION = 2
_TEST_HIGH_SCORE = 4
_TEST_LOW_SCORE = 2
class GetProposalsToBeAcceptedForOrgTest(unittest.TestCase):
"""Unit tests for getProposalsToBeAcceptedForOrg function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.program = program_utils.seedGSoCProgram()
self.org = org_utils.seedSOCOrganization(self.program.key())
def testOrganizationWithNoSlotsAvailable(self):
"""Tests for organization that have used all its slots."""
self.org.slot_allocation = _TEST_SLOT_ALLOCATION
# seed proposals for slots which have been taken
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.ACCEPTED)
proposals = proposal_logic.getProposalsToBeAcceptedForOrg(self.org)
self.assertListEqual(proposals, [])
def testOrganizationWithSlotsAvailable(self):
"""Tests for organization that still have slots available for projects."""
self.org.slot_allocation = _TEST_SLOT_ALLOCATION * 2
# seed proposals for slots which have been taken
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.ACCEPTED)
# seed proposals to be accepted for the organization
expected_keys = set()
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=True).key)
# seed proposals to be rejected for the organization
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=False)
proposals = proposal_logic.getProposalsToBeAcceptedForOrg(self.org)
actual_keys = set(proposal.key for proposal in proposals)
self.assertSetEqual(expected_keys, actual_keys)
def testOrganizationWithTooManyProposalsToAccept(self):
"""Tests for organization that have accepted more proposals than slots."""
self.org.slot_allocation = _TEST_SLOT_ALLOCATION
mentor = profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key])
# seed additional proposals marked as to be accepted for the organization
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
score = proposal_model.Score(author=mentor.key, value=_TEST_LOW_SCORE)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=True,
scores=[score])
# seed proposals to be accepted for the organization
expected_keys = set()
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
score = proposal_model.Score(author=mentor.key, value=_TEST_HIGH_SCORE)
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=True,
scores=[score]).key)
proposals = proposal_logic.getProposalsToBeAcceptedForOrg(self.org)
actual_keys = set(proposal.key for proposal in proposals)
self.assertSetEqual(expected_keys, actual_keys)
def testProposalsWithNoMentorsAreNotToBeAccepted(self):
"""Tests that proposals with no mentors are not returned."""
self.org.slot_allocation = _TEST_SLOT_ALLOCATION
mentor = profile_utils.seedNDBProfile(
self.program.key(), mentor_for=[self.org.key])
# seed proposals with higher scores but with no mentors
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
score = proposal_model.Score(author=mentor.key, value=_TEST_HIGH_SCORE)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=True,
scores=[score], mentors=[])
# seed proposals to be accepted for the organization
expected_keys = set()
for _ in range(_TEST_SLOT_ALLOCATION):
student = profile_utils.seedNDBStudent(self.program)
score = proposal_model.Score(author=mentor.key, value=_TEST_LOW_SCORE)
expected_keys.add(
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.PENDING, accept_as_project=True,
scores=[score]).key)
proposals = proposal_logic.getProposalsToBeAcceptedForOrg(self.org)
actual_keys = set(proposal.key for proposal in proposals)
self.assertSetEqual(expected_keys, actual_keys)
_TEST_NUMBER_OF_PROPOSALS = 3
class GetUsedSlotsTest(test_utils.GSoCTestCase):
"""Unit tests for getUsedSlots function."""
def setUp(self):
"""See unittest.TestCase.setUp for specification."""
self.init()
self.other_org = org_utils.seedSOCOrganization(self.program.key())
def testAfterStudentsAnnounced(self):
"""Tests that number of accepted proposals is returned."""
self.timeline_helper.studentsAnnounced()
# seed a couple of accepted proposals
for _ in range(_TEST_NUMBER_OF_PROPOSALS):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.ACCEPTED)
# seed a couple of rejected proposals
for _ in range(_TEST_NUMBER_OF_PROPOSALS):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
status=proposal_model.Status.REJECTED)
# seed a couple of accepted proposals for the other organization
for _ in range(_TEST_NUMBER_OF_PROPOSALS):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.other_org.key,
status=proposal_model.Status.ACCEPTED)
used_slots = proposal_logic.getUsedSlots(
self.org.key, self.program.timeline)
self.assertEqual(used_slots, _TEST_NUMBER_OF_PROPOSALS)
def testBeforeStudentsAnnounced(self):
"""Tests that number valid proposals is returned."""
self.timeline_helper.studentSignup()
# seed a couple of to-be-accepted proposals
for _ in range(_TEST_NUMBER_OF_PROPOSALS):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
accept_as_project=True)
# seed a couple of not to-be-accepted proposals
for _ in range(_TEST_NUMBER_OF_PROPOSALS):
student = profile_utils.seedNDBStudent(self.program)
proposal_utils.seedNDBProposal(
student.key, self.program.key(), org_key=self.org.key,
accept_as_project=False)
used_slots = proposal_logic.getUsedSlots(
self.org.key, self.program.timeline)
self.assertEqual(used_slots, _TEST_NUMBER_OF_PROPOSALS)