Also added datadog application to commom django apps and set it to use a local statsd server by default.
381 lines
17 KiB
Python
381 lines
17 KiB
Python
'''
|
|
Created on Jan 17, 2013
|
|
|
|
@author: brian
|
|
'''
|
|
import logging
|
|
import os
|
|
from tempfile import mkdtemp
|
|
import cStringIO
|
|
import shutil
|
|
import sys
|
|
|
|
from django.test import TestCase
|
|
from django.core.management import call_command
|
|
from nose.plugins.skip import SkipTest
|
|
|
|
from student.models import User, TestCenterUser, get_testcenter_registration
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def create_tc_user(username):
|
|
user = User.objects.create_user(username, '{}@edx.org'.format(username), 'fakepass')
|
|
options = {
|
|
'first_name': 'TestFirst',
|
|
'last_name': 'TestLast',
|
|
'address_1': 'Test Address',
|
|
'city': 'TestCity',
|
|
'state': 'Alberta',
|
|
'postal_code': 'A0B 1C2',
|
|
'country': 'CAN',
|
|
'phone': '252-1866',
|
|
'phone_country_code': '1',
|
|
}
|
|
call_command('pearson_make_tc_user', username, **options)
|
|
return TestCenterUser.objects.get(user=user)
|
|
|
|
|
|
def create_tc_registration(username, course_id='org1/course1/term1', exam_code='exam1', accommodation_code=None):
|
|
|
|
options = {'exam_series_code': exam_code,
|
|
'eligibility_appointment_date_first': '2013-01-01T00:00',
|
|
'eligibility_appointment_date_last': '2013-12-31T23:59',
|
|
'accommodation_code': accommodation_code,
|
|
'create_dummy_exam': True,
|
|
}
|
|
|
|
call_command('pearson_make_tc_registration', username, course_id, **options)
|
|
user = User.objects.get(username=username)
|
|
registrations = get_testcenter_registration(user, course_id, exam_code)
|
|
return registrations[0]
|
|
|
|
|
|
def create_multiple_registrations(prefix='test'):
|
|
username1 = '{}_multiple1'.format(prefix)
|
|
create_tc_user(username1)
|
|
create_tc_registration(username1)
|
|
create_tc_registration(username1, course_id='org1/course2/term1')
|
|
create_tc_registration(username1, exam_code='exam2')
|
|
username2 = '{}_multiple2'.format(prefix)
|
|
create_tc_user(username2)
|
|
create_tc_registration(username2)
|
|
username3 = '{}_multiple3'.format(prefix)
|
|
create_tc_user(username3)
|
|
create_tc_registration(username3, course_id='org1/course2/term1')
|
|
username4 = '{}_multiple4'.format(prefix)
|
|
create_tc_user(username4)
|
|
create_tc_registration(username4, exam_code='exam2')
|
|
|
|
|
|
def get_command_error_text(*args, **options):
|
|
stderr_string = None
|
|
old_stderr = sys.stderr
|
|
sys.stderr = cStringIO.StringIO()
|
|
try:
|
|
call_command(*args, **options)
|
|
except SystemExit, why1:
|
|
# The goal here is to catch CommandError calls.
|
|
# But these are actually translated into nice messages,
|
|
# and sys.exit(1) is then called. For testing, we
|
|
# want to catch what sys.exit throws, and get the
|
|
# relevant text either from stdout or stderr.
|
|
if (why1.message > 0):
|
|
stderr_string = sys.stderr.getvalue()
|
|
else:
|
|
raise why1
|
|
except Exception, why:
|
|
raise why
|
|
|
|
finally:
|
|
sys.stderr = old_stderr
|
|
|
|
if stderr_string is None:
|
|
raise Exception("Expected call to {} to fail, but it succeeded!".format(args[0]))
|
|
return stderr_string
|
|
|
|
|
|
def get_error_string_for_management_call(*args, **options):
|
|
stdout_string = None
|
|
old_stdout = sys.stdout
|
|
old_stderr = sys.stderr
|
|
sys.stdout = cStringIO.StringIO()
|
|
sys.stderr = cStringIO.StringIO()
|
|
try:
|
|
call_command(*args, **options)
|
|
except SystemExit, why1:
|
|
# The goal here is to catch CommandError calls.
|
|
# But these are actually translated into nice messages,
|
|
# and sys.exit(1) is then called. For testing, we
|
|
# want to catch what sys.exit throws, and get the
|
|
# relevant text either from stdout or stderr.
|
|
if (why1.message == 1):
|
|
stdout_string = sys.stdout.getvalue()
|
|
stderr_string = sys.stderr.getvalue()
|
|
else:
|
|
raise why1
|
|
except Exception, why:
|
|
raise why
|
|
|
|
finally:
|
|
sys.stdout = old_stdout
|
|
sys.stderr = old_stderr
|
|
|
|
if stdout_string is None:
|
|
raise Exception("Expected call to {} to fail, but it succeeded!".format(args[0]))
|
|
return stdout_string, stderr_string
|
|
|
|
|
|
def get_file_info(dirpath):
|
|
filelist = os.listdir(dirpath)
|
|
print 'Files found: {}'.format(filelist)
|
|
numfiles = len(filelist)
|
|
if numfiles == 1:
|
|
filepath = os.path.join(dirpath, filelist[0])
|
|
with open(filepath, 'r') as cddfile:
|
|
filecontents = cddfile.readlines()
|
|
numlines = len(filecontents)
|
|
return filepath, numlines
|
|
else:
|
|
raise Exception("Expected to find a single file in {}, but found {}".format(dirpath, filelist))
|
|
|
|
|
|
class PearsonTestCase(TestCase):
|
|
'''
|
|
Base class for tests running Pearson-related commands
|
|
'''
|
|
|
|
def assertErrorContains(self, error_message, expected):
|
|
self.assertTrue(error_message.find(expected) >= 0, 'error message "{}" did not contain "{}"'.format(error_message, expected))
|
|
|
|
def setUp(self):
|
|
self.import_dir = mkdtemp(prefix="import")
|
|
self.addCleanup(shutil.rmtree, self.import_dir)
|
|
self.export_dir = mkdtemp(prefix="export")
|
|
self.addCleanup(shutil.rmtree, self.export_dir)
|
|
|
|
def tearDown(self):
|
|
pass
|
|
# and clean up the database:
|
|
# TestCenterUser.objects.all().delete()
|
|
# TestCenterRegistration.objects.all().delete()
|
|
|
|
|
|
class PearsonCommandTestCase(PearsonTestCase):
|
|
|
|
def test_missing_demographic_fields(self):
|
|
# We won't bother to test all details of form validation here.
|
|
# It is enough to show that it works here, but deal with test cases for the form
|
|
# validation in the student tests, not these management tests.
|
|
username = 'baduser'
|
|
User.objects.create_user(username, '{}@edx.org'.format(username), 'fakepass')
|
|
options = {}
|
|
error_string = get_command_error_text('pearson_make_tc_user', username, **options)
|
|
self.assertTrue(error_string.find('Field Form errors encountered:') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: city') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: first_name') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: last_name') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: country') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: phone_country_code') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: phone') >= 0)
|
|
self.assertTrue(error_string.find('Field Form Error: address_1') >= 0)
|
|
self.assertErrorContains(error_string, 'Field Form Error: address_1')
|
|
|
|
def test_create_good_testcenter_user(self):
|
|
testcenter_user = create_tc_user("test_good_user")
|
|
self.assertIsNotNone(testcenter_user)
|
|
|
|
def test_create_good_testcenter_registration(self):
|
|
username = 'test_good_registration'
|
|
create_tc_user(username)
|
|
registration = create_tc_registration(username)
|
|
self.assertIsNotNone(registration)
|
|
|
|
def test_cdd_missing_option(self):
|
|
error_string = get_command_error_text('pearson_export_cdd', **{})
|
|
self.assertErrorContains(error_string, 'Error: --destination or --dest-from-settings must be used')
|
|
|
|
def test_ead_missing_option(self):
|
|
error_string = get_command_error_text('pearson_export_ead', **{})
|
|
self.assertErrorContains(error_string, 'Error: --destination or --dest-from-settings must be used')
|
|
|
|
def test_export_single_cdd(self):
|
|
# before we generate any tc_users, we expect there to be nothing to output:
|
|
options = {'dest-from-settings': True}
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir}):
|
|
call_command('pearson_export_cdd', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 1, "Expect cdd file to have no non-header lines")
|
|
os.remove(filepath)
|
|
|
|
# generating a tc_user should result in a line in the output
|
|
username = 'test_single_cdd'
|
|
create_tc_user(username)
|
|
call_command('pearson_export_cdd', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 2, "Expect cdd file to have one non-header line")
|
|
os.remove(filepath)
|
|
|
|
# output after registration should not have any entries again.
|
|
call_command('pearson_export_cdd', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 1, "Expect cdd file to have no non-header lines")
|
|
os.remove(filepath)
|
|
|
|
# if we modify the record, then it should be output again:
|
|
user_options = {'first_name': 'NewTestFirst', }
|
|
call_command('pearson_make_tc_user', username, **user_options)
|
|
call_command('pearson_export_cdd', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 2, "Expect cdd file to have one non-header line")
|
|
os.remove(filepath)
|
|
|
|
def test_export_single_ead(self):
|
|
# before we generate any registrations, we expect there to be nothing to output:
|
|
options = {'dest-from-settings': True}
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir}):
|
|
call_command('pearson_export_ead', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 1, "Expect ead file to have no non-header lines")
|
|
os.remove(filepath)
|
|
|
|
# generating a registration should result in a line in the output
|
|
username = 'test_single_ead'
|
|
create_tc_user(username)
|
|
create_tc_registration(username)
|
|
call_command('pearson_export_ead', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 2, "Expect ead file to have one non-header line")
|
|
os.remove(filepath)
|
|
|
|
# output after registration should not have any entries again.
|
|
call_command('pearson_export_ead', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 1, "Expect ead file to have no non-header lines")
|
|
os.remove(filepath)
|
|
|
|
# if we modify the record, then it should be output again:
|
|
create_tc_registration(username, accommodation_code='EQPMNT')
|
|
call_command('pearson_export_ead', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 2, "Expect ead file to have one non-header line")
|
|
os.remove(filepath)
|
|
|
|
def test_export_multiple(self):
|
|
create_multiple_registrations("export")
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir}):
|
|
options = {'dest-from-settings': True}
|
|
call_command('pearson_export_cdd', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 5, "Expect cdd file to have four non-header lines: total was {}".format(numlines))
|
|
os.remove(filepath)
|
|
|
|
call_command('pearson_export_ead', **options)
|
|
(filepath, numlines) = get_file_info(self.export_dir)
|
|
self.assertEquals(numlines, 7, "Expect ead file to have six non-header lines: total was {}".format(numlines))
|
|
os.remove(filepath)
|
|
|
|
|
|
# def test_bad_demographic_option(self):
|
|
# username = 'nonuser'
|
|
# output_string, stderrmsg = get_error_string_for_management_call('pearson_make_tc_user', username, **{'--garbage' : None })
|
|
# print stderrmsg
|
|
# self.assertErrorContains(stderrmsg, 'Unexpected option')
|
|
#
|
|
# def test_missing_demographic_user(self):
|
|
# username = 'nonuser'
|
|
# output_string, error_string = get_error_string_for_management_call('pearson_make_tc_user', username, **{})
|
|
# self.assertErrorContains(error_string, 'User matching query does not exist')
|
|
|
|
# credentials for a test SFTP site:
|
|
SFTP_HOSTNAME = 'ec2-23-20-150-101.compute-1.amazonaws.com'
|
|
SFTP_USERNAME = 'pearsontest'
|
|
SFTP_PASSWORD = 'password goes here'
|
|
|
|
S3_BUCKET = 'edx-pearson-archive'
|
|
AWS_ACCESS_KEY_ID = 'put yours here'
|
|
AWS_SECRET_ACCESS_KEY = 'put yours here'
|
|
|
|
|
|
class PearsonTransferTestCase(PearsonTestCase):
|
|
'''
|
|
Class for tests running Pearson transfers
|
|
'''
|
|
|
|
def test_transfer_config(self):
|
|
stderrmsg = get_command_error_text('pearson_transfer', **{'mode': 'garbage'})
|
|
self.assertErrorContains(stderrmsg, 'Error: No PEARSON entries')
|
|
|
|
stderrmsg = get_command_error_text('pearson_transfer')
|
|
self.assertErrorContains(stderrmsg, 'Error: No PEARSON entries')
|
|
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir,
|
|
'LOCAL_IMPORT': self.import_dir}):
|
|
stderrmsg = get_command_error_text('pearson_transfer')
|
|
self.assertErrorContains(stderrmsg, 'Error: No entry in the PEARSON settings')
|
|
|
|
def test_transfer_export_missing_dest_dir(self):
|
|
raise SkipTest()
|
|
create_multiple_registrations('export_missing_dest')
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir,
|
|
'SFTP_EXPORT': 'this/does/not/exist',
|
|
'SFTP_HOSTNAME': SFTP_HOSTNAME,
|
|
'SFTP_USERNAME': SFTP_USERNAME,
|
|
'SFTP_PASSWORD': SFTP_PASSWORD,
|
|
'S3_BUCKET': S3_BUCKET,
|
|
},
|
|
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID,
|
|
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY):
|
|
options = {'mode': 'export'}
|
|
stderrmsg = get_command_error_text('pearson_transfer', **options)
|
|
self.assertErrorContains(stderrmsg, 'Error: SFTP destination path does not exist')
|
|
|
|
def test_transfer_export(self):
|
|
raise SkipTest()
|
|
create_multiple_registrations("transfer_export")
|
|
with self.settings(PEARSON={'LOCAL_EXPORT': self.export_dir,
|
|
'SFTP_EXPORT': 'results/topvue',
|
|
'SFTP_HOSTNAME': SFTP_HOSTNAME,
|
|
'SFTP_USERNAME': SFTP_USERNAME,
|
|
'SFTP_PASSWORD': SFTP_PASSWORD,
|
|
'S3_BUCKET': S3_BUCKET,
|
|
},
|
|
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID,
|
|
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY):
|
|
options = {'mode': 'export'}
|
|
# call_command('pearson_transfer', **options)
|
|
# # confirm that the export directory is still empty:
|
|
# self.assertEqual(len(os.listdir(self.export_dir)), 0, "expected export directory to be empty")
|
|
|
|
def test_transfer_import_missing_source_dir(self):
|
|
raise SkipTest()
|
|
create_multiple_registrations('import_missing_src')
|
|
with self.settings(PEARSON={'LOCAL_IMPORT': self.import_dir,
|
|
'SFTP_IMPORT': 'this/does/not/exist',
|
|
'SFTP_HOSTNAME': SFTP_HOSTNAME,
|
|
'SFTP_USERNAME': SFTP_USERNAME,
|
|
'SFTP_PASSWORD': SFTP_PASSWORD,
|
|
'S3_BUCKET': S3_BUCKET,
|
|
},
|
|
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID,
|
|
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY):
|
|
options = {'mode': 'import'}
|
|
stderrmsg = get_command_error_text('pearson_transfer', **options)
|
|
self.assertErrorContains(stderrmsg, 'Error: SFTP source path does not exist')
|
|
|
|
def test_transfer_import(self):
|
|
raise SkipTest()
|
|
create_multiple_registrations('import_missing_src')
|
|
with self.settings(PEARSON={'LOCAL_IMPORT': self.import_dir,
|
|
'SFTP_IMPORT': 'results',
|
|
'SFTP_HOSTNAME': SFTP_HOSTNAME,
|
|
'SFTP_USERNAME': SFTP_USERNAME,
|
|
'SFTP_PASSWORD': SFTP_PASSWORD,
|
|
'S3_BUCKET': S3_BUCKET,
|
|
},
|
|
AWS_ACCESS_KEY_ID=AWS_ACCESS_KEY_ID,
|
|
AWS_SECRET_ACCESS_KEY=AWS_SECRET_ACCESS_KEY):
|
|
options = {'mode': 'import'}
|
|
call_command('pearson_transfer', **options)
|
|
self.assertEqual(len(os.listdir(self.import_dir)), 0, "expected import directory to be empty")
|