Changeset 7240 for main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests
- Timestamp:
- 30 Nov 2011, 23:13:26 (13 years ago)
- Location:
- main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_applicant.py
r7193 r7240 32 32 from waeup.sirp.interfaces import IFileStoreHandler, IFileStoreNameChooser 33 33 from waeup.sirp.applicants import ( 34 ResultEntry, Applicant, ApplicantFactory, get_regno_or_ac,34 Applicant, ApplicantFactory, ApplicantsContainer, 35 35 ApplicantImageStoreHandler, ApplicantImageNameChooser, 36 36 ) 37 from waeup.sirp.applicants.interfaces import I ResultEntry, IApplicant37 from waeup.sirp.applicants.interfaces import IApplicant 38 38 from waeup.sirp.testing import FunctionalTestCase, FunctionalLayer 39 39 … … 56 56 super(HelperTests, self).tearDown() 57 57 shutil.rmtree(self.workdir) 58 return59 60 def test_get_regno_or_ac(self):61 # we can get reg_no or access_code of an applicants if it is set62 appl1 = Applicant()63 appl2 = Applicant()64 appl2.reg_no = u'foo'65 appl3 = Applicant()66 appl3.access_code = u'bar'67 appl4 = Applicant()68 appl4.reg_no = u'foo'69 appl4.access_code = u'bar'70 self.assertTrue(71 get_regno_or_ac(appl1) is None)72 self.assertEqual(73 get_regno_or_ac(appl2), u'foo')74 self.assertEqual(75 get_regno_or_ac(appl3), u'bar')76 self.assertEqual(77 get_regno_or_ac(appl4), u'foo')78 58 return 79 59 … … 124 104 return 125 105 126 class ApplicantImageNameChooserTests(FunctionalTestCase):127 128 layer = FunctionalLayer129 130 def test_iface(self):131 # make sure we implement promised interfaces132 obj = ApplicantImageNameChooser(None) # needs a context normally133 verify.verifyClass(IFileStoreNameChooser, ApplicantImageNameChooser)134 verify.verifyObject(IFileStoreNameChooser, obj)135 return136 137 def test_name_chooser_available(self):138 # we can get a name chooser for applicant objects as adapter139 appl = Applicant()140 chooser = IFileStoreNameChooser(appl)141 self.assertTrue(chooser is not None)142 return143 144 def test_name_chooser_applicant_wo_container(self):145 # we can get an image filename for applicants not in a container146 appl = Applicant()147 appl.reg_no = u'MY_REG_NO'148 chooser = IFileStoreNameChooser(appl)149 result = chooser.chooseName()150 # the file would be stored in a ``_default`` directory.151 self.assertEqual(152 result, '__img-applicant___default/MY_REG_NO.jpg')153 return154 155 def test_name_chooser_applicant_w_container(self):156 appl = Applicant()157 appl.reg_no = u'MY_REG_NO'158 fake_container = grok.Container()159 fake_container.__name__ = 'folder'160 fake_container['appl'] = appl161 appl.__parent__ = fake_container162 chooser = IFileStoreNameChooser(appl)163 result = chooser.chooseName()164 self.assertEqual(165 result, '__img-applicant__folder/MY_REG_NO.jpg')166 return167 168 def test_name_chooser_check_name(self):169 # we can check file ids for applicants170 appl = Applicant()171 appl.reg_no = u'MY_REG_NO'172 fake_container = grok.Container()173 fake_container.__name__ = 'folder'174 fake_container['appl'] = appl175 appl.__parent__ = fake_container176 chooser = IFileStoreNameChooser(appl)177 result1 = chooser.checkName('foo')178 result2 = chooser.checkName('__img-applicant__folder/MY_REG_NO.jpg')179 self.assertEqual(result1, False)180 self.assertEqual(result2, True)181 return182 183 class ResultEntryTest(unittest.TestCase):184 185 def setUp(self):186 self.result_entry = ResultEntry()187 return188 189 def tearDown(self):190 pass191 192 def test_interfaces(self):193 verify.verifyClass(IResultEntry, ResultEntry)194 verify.verifyObject(IResultEntry, self.result_entry)195 196 def test_resultentry(self):197 entry = ResultEntry('Some subject', 3.7)198 assert entry.subject == 'Some subject'199 assert entry.score == 3.7200 201 106 class ApplicantTest(FunctionalTestCase): 202 107 … … 235 140 236 141 def test_factory(self): 237 obj = self.factory( )142 obj = self.factory(container=None) 238 143 assert isinstance(obj, Applicant) 239 144 -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_authentication.py
r7238 r7240 16 16 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 17 ## 18 import shutil19 import tempfile20 18 import unittest 21 19 from zope.authentication.interfaces import IAuthentication 22 from zope.component import provideAdapter, getUtility, queryUtility 23 from zope.component.hooks import setSite 24 from zope.interface import verify 25 from zope.pluggableauth.interfaces import IAuthenticatedPrincipalFactory 26 from zope.publisher.browser import TestRequest 27 from zope.publisher.interfaces import IRequest 28 from zope.session.interfaces import ISession 29 from zope.testbrowser.testing import Browser 30 from zope.testing import cleanup 31 from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase 32 from waeup.sirp.app import University 33 from waeup.sirp.interfaces import IAuthPluginUtility 34 from waeup.sirp.applicants import ApplicantsContainer, Applicant 20 from zope.component import provideUtility, queryUtility, getGlobalSiteManager 21 from zope.interface.verify import verifyClass, verifyObject 22 from zope.password.password import SSHAPasswordManager 23 from zope.password.interfaces import IPasswordManager 24 from zope.pluggableauth import PluggableAuthentication 25 from zope.security.interfaces import Unauthorized 26 from zope.securitypolicy.role import Role 27 from zope.securitypolicy.interfaces import IRole, Allow 28 from waeup.sirp.authentication import get_principal_role_manager 29 from waeup.sirp.interfaces import IAuthPluginUtility, IUserAccount 35 30 from waeup.sirp.applicants.authentication import ( 36 ApplicantsAuthenticatorPlugin, WAeUPApplicantCredentialsPlugin, 37 ApplicantCredentials, AuthenticatedApplicantPrincipalFactory, 38 ApplicantPrincipalInfo, ApplicantPrincipal, ApplicantsAuthenticatorSetup) 31 ApplicantsAuthenticatorSetup, ApplicantAccount) 32 from waeup.sirp.applicants.tests.test_browser import ApplicantsFullSetup 33 from waeup.sirp.testing import FunctionalLayer 34 35 class ApplicantsAuthenticatorSetupTests(unittest.TestCase): 36 37 def test_iface(self): 38 obj = ApplicantsAuthenticatorSetup() 39 verifyClass(IAuthPluginUtility, ApplicantsAuthenticatorSetup) 40 verifyObject(IAuthPluginUtility, obj) 41 return 42 43 def test_register(self): 44 # Make sure registration works. 45 setup = ApplicantsAuthenticatorSetup() 46 pau = PluggableAuthentication() 47 setup.register(pau) 48 self.assertTrue('applicants' in pau.authenticatorPlugins) 49 return 50 51 def test_unregister(self): 52 # Make sure deregistration works. 53 setup = ApplicantsAuthenticatorSetup() 54 pau = PluggableAuthentication() 55 pau.authenticatorPlugins = ('applicants') 56 setup.unregister(pau) 57 self.assertTrue('applicants' not in pau.authenticatorPlugins) 58 return 39 59 40 60 41 class FakeBatch(dict): 42 def getAccessCode(self, id): 43 return self.get(id) 61 class FakeApplicant(object): 62 applicant_id = 'test_appl' 63 fullname = 'Tilman Gause' 64 password = None 65 email = None 66 phone = None 44 67 45 class FakeAccessCode(object):46 def __init__(self, repr, state = 'initialized'):47 self.representation = repr48 self.state = state49 68 50 class FakeApplication(object):51 def __init__(self, ac=None):52 self.access_code = ac69 class MinimalPAU(PluggableAuthentication): 70 def getPrincipal(self, id): 71 return 'faked principal' 53 72 54 def FakeSite(): 55 return { 56 'applicants': { 57 'APP': { 58 'APP-12345': FakeApplication(u'APP-12345'), 59 'APP-54321': FakeApplication(u'APP-54321'), 60 'APP-22222': FakeApplication(u'APP-OTHER'), 61 'APP-44444': FakeApplication(), 62 'APP-55555': FakeApplication(u'APP-OTHER'), 63 'APP-77777': FakeApplication(u'APP-77777'), 64 }, 65 }, 66 'accesscodes': { 67 'APP': FakeBatch({ 68 'APP-12345': FakeAccessCode('APP-12345'), 69 'APP-54321': FakeAccessCode('APP-54321', 'used'), 70 'APP-11111': FakeAccessCode('APP-11111'), 71 'APP-22222': FakeAccessCode('APP-22222'), 72 'APP-33333': FakeAccessCode('APP-33333', 'used'), 73 'APP-44444': FakeAccessCode('APP-44444', 'used'), 74 'APP-55555': FakeAccessCode('APP-55555', 'used'), 75 'APP-66666': FakeAccessCode('APP-66666', 'disabled'), 76 'APP-77777': FakeAccessCode('APP-77777', 'disabled'), 77 }) 78 } 79 } 80 81 class AuthenticatorPluginTest(FunctionalTestCase): 82 83 layer = FunctionalLayer 84 85 def create_applicant(self, cname, aname, ac=None): 86 # Create an applicant in an applicants container 87 setSite(self.app) 88 if not cname in self.app['applicants'].keys(): 89 container = ApplicantsContainer() 90 self.app['applicants'][cname] = container 91 applicant = Applicant() 92 if ac is not None: 93 applicant.access_code = ac 94 self.app['applicants'][cname][aname] = applicant 95 return 73 class ApplicantAccountTests(unittest.TestCase): 96 74 97 75 def setUp(self): 98 super(AuthenticatorPluginTest, self).setUp() 76 self.fake_stud = FakeApplicant() 77 self.account = ApplicantAccount(self.fake_stud) 99 78 100 # Setup a sample site for each test 101 app = University() 102 self.dc_root = tempfile.mkdtemp() 103 app['datacenter'].setStoragePath(self.dc_root) 79 # We provide a minimal PAU 80 pau = MinimalPAU() 81 provideUtility(pau, IAuthentication) 104 82 105 # Prepopulate the ZODB...106 self.getRootFolder()['app'] = app107 self.app = self.getRootFolder()['app']83 # We register a role 84 test_role = Role('waeup.test.Role', 'Testing Role') 85 provideUtility(test_role, IRole, name='waeup.test.Role') 108 86 109 fake_site = FakeSite() 110 for ckey, fake_container in fake_site['applicants'].items(): 111 for akey, fake_appl in fake_container.items(): 112 self.create_applicant(ckey, akey, fake_appl.access_code) 113 del self.app['accesscodes'] 114 self.app['accesscodes'] = fake_site['accesscodes'] 115 116 self.plugin = ApplicantsAuthenticatorPlugin() 87 # We have to setup a password manager utility manually as we 88 # have no functional test. In functional tests this would 89 # happen automatically, but it would take a lot more time to 90 # run the tests. 91 provideUtility( 92 SSHAPasswordManager(), IPasswordManager, 'SSHA') 117 93 return 118 94 119 95 def tearDown(self): 120 super(AuthenticatorPluginTest, self).tearDown() 121 shutil.rmtree(self.dc_root) 96 self.account.roles = [] # make sure roles are reset 97 gsm = getGlobalSiteManager() 98 to_clean = [] 99 # Clear up utilities registered in setUp 100 to_clean.append( 101 (IPasswordManager, queryUtility( 102 IPasswordManager, name='SSHA', default=None))) 103 to_clean.append( 104 (IAuthentication, queryUtility(IAuthentication, default=None))) 105 to_clean.append( 106 (IRole, queryUtility(IRole, name='test.Role', default=None))) 107 for iface, elem in to_clean: 108 if elem is not None: 109 gsm.unregisterUtility(elem, iface) 122 110 return 123 111 124 def test_invalid_credentials(self): 125 result = self.plugin.authenticateCredentials('not-a-dict') 126 assert result is None 127 128 result = self.plugin.authenticateCredentials( 129 dict(accesscode=None, foo='blah')) 130 assert result is None 131 132 result = self.plugin.authenticateCredentials( 133 dict(accesscode='Nonsense',)) 134 assert result is None 135 136 # Possible cases, where formal correct authentication 137 # data is not valid: 138 result = self.plugin.authenticateCredentials( 139 dict(accesscode='APP-33333')) 140 assert result is None 141 142 result = self.plugin.authenticateCredentials( 143 dict(accesscode='APP-55555')) 144 assert result is None 145 146 result = self.plugin.authenticateCredentials( 147 dict(accesscode='APP-66666')) 148 assert result is None 149 150 result = self.plugin.authenticateCredentials( 151 dict(accesscode='APP-77777')) 152 assert result is None 153 112 def test_iface(self): 113 verifyClass(IUserAccount, ApplicantAccount) 114 verifyObject(IUserAccount, self.account) 154 115 return 155 116 156 def test_valid_credentials(self): 157 """The six different cases where we allow login. 158 159 All other combinations should be forbidden. 160 """ 161 result = self.plugin.authenticateCredentials( 162 dict(accesscode='APP-11111')) 163 assert result is not None 164 165 result = self.plugin.authenticateCredentials( 166 dict(accesscode='APP-12345')) 167 assert result is not None 168 169 result = self.plugin.authenticateCredentials( 170 dict(accesscode='APP-54321')) 171 assert result is not None 172 173 # check the `principalInfo` method of authenticator 174 # plugin. This is only here to satisfy the coverage report. 175 assert self.plugin.principalInfo('not-an-id') is None 117 def test_set_password(self): 118 # make sure we can set a password. 119 self.account.setPassword('secret') 120 self.assertTrue(self.fake_stud.password is not None) 121 # we do not store plaintext passwords 122 self.assertTrue(self.fake_stud.password != 'secret') 123 # passwords are stored as unicode 124 self.assertTrue(isinstance(self.fake_stud.password, unicode)) 176 125 return 177 126 178 session_data = { 179 'zope.pluggableauth.browserplugins': {} 180 } 181 182 class FakeSession(dict): 183 def __init__(self, request): 184 pass 185 186 def get(self, key, default=None): 187 return self.__getitem__(key, default) 188 189 def __getitem__(self, key, default=None): 190 return session_data.get(key, default) 191 192 def __setitem__(self, key, value): 193 session_data[key] = value 127 def test_check_password(self): 128 # make sure we can check a password. 129 self.account.setPassword('secret') 130 result1 = self.account.checkPassword(None) 131 result2 = self.account.checkPassword('nonsense') 132 result3 = self.account.checkPassword('secret') 133 self.assertEqual(result1, False) 134 self.assertEqual(result2, False) 135 self.assertEqual(result3, True) 194 136 return 195 137 196 class CredentialsPluginTest(unittest.TestCase):197 198 def setUp(self):199 self.request = TestRequest()200 provideAdapter(FakeSession, (IRequest,), ISession)201 self.plugin = WAeUPApplicantCredentialsPlugin()202 self. full_request = TestRequest()203 se ssion_data['zope.pluggableauth.browserplugins'] = {}138 def test_check_unset_password(self): 139 # empty and unset passwords do not match anything 140 self.fake_stud.password = None 141 result1 = self.account.checkPassword('') 142 self.fake_stud.password = '' 143 result2 = self.account.checkPassword('') 144 self.assertEqual(result1, False) 145 self.assertEqual(result2, False) 204 146 return 205 147 206 def tearDown(self): 207 cleanup.tearDown() 148 def test_check_password_no_string(self): 149 # if passed in password is not a string, we gain no access 150 self.fake_stud.password = 'secret' 151 result1 = self.account.checkPassword(None) 152 result2 = self.account.checkPassword(object()) 153 self.assertEqual(result1, False) 154 self.assertEqual(result2, False) 208 155 return 209 156 210 def filled_request(self, form_dict): 211 request = TestRequest() 212 for key, value in form_dict.items(): 213 request.form[key] = value 214 return request 215 216 def test_extractCredentials_invalid(self): 217 result = self.plugin.extractCredentials('not-a-request') 218 assert result is None 157 def test_role_set(self): 158 # make sure we can set roles for principals denoted by account 159 prm = get_principal_role_manager() 160 self.assertEqual(prm.getPrincipalsAndRoles(), []) 161 self.account.roles = ['waeup.test.Role'] 162 self.assertEqual( 163 prm.getPrincipalsAndRoles(), 164 [('waeup.test.Role', 'test_appl', Allow)]) 219 165 return 220 166 221 def test_extractCredentials_empty(self): 222 result = self.plugin.extractCredentials(self.request) 223 assert result is None 167 def test_role_get(self): 168 # make sure we can get roles set for an account 169 self.assertEqual(self.account.roles, []) 170 self.account.roles = ['waeup.test.Role',] # set a role 171 self.assertEqual(self.account.roles, ['waeup.test.Role']) 224 172 return 225 173 226 def test_extractCredentials_full_set(self): 227 request = self.filled_request({ 228 'form.ac_prefix': 'APP', 229 'form.ac_series': '1', 230 'form.ac_number': '1234567890', 231 #'form.jamb_reg_no': 'JAMB_NUMBER', 232 }) 233 result = self.plugin.extractCredentials(request) 234 self.assertEqual(result, {'accesscode': 'APP-1-1234567890'}) 235 return 236 237 def test_extractCredentials_accesscode_only(self): 238 request = self.filled_request({ 239 'form.ac_prefix': 'APP', 240 'form.ac_series': '1', 241 'form.ac_number': '1234567890', 242 }) 243 result = self.plugin.extractCredentials(request) 244 self.assertEqual(result, {'accesscode': 'APP-1-1234567890'}) 245 return 246 247 def test_extractCredentials_from_empty_session(self): 248 session_data['zope.pluggableauth.browserplugins']['credentials'] = None 249 result = self.plugin.extractCredentials(self.request) 250 assert result is None 251 return 252 253 def test_extractCredentials_from_nonempty_session(self): 254 credentials = ApplicantCredentials('APP-1-12345') 255 session_data['zope.pluggableauth.browserplugins'][ 256 'credentials'] = credentials 257 result = self.plugin.extractCredentials(self.request) 258 self.assertEqual(result, {'accesscode': 'APP-1-12345'}) 259 return 260 261 262 class ApplicantCredentialsTest(unittest.TestCase): 263 264 def setUp(self): 265 self.credentials = ApplicantCredentials('SOME_ACCESSCODE') 266 return 267 268 def tearDown(self): 269 return 270 271 def test_methods(self): 272 self.assertEqual(self.credentials.getAccessCode(), 'SOME_ACCESSCODE') 273 assert self.credentials.getLogin() is None 274 assert self.credentials.getPassword() is None 275 return 276 277 class FakePluggableAuth(object): 278 prefix = 'foo' 279 280 class PrincipalFactoryTest(unittest.TestCase): 281 282 def setUp(self): 283 self.info = ApplicantPrincipalInfo('APP-1-1234567890') 284 return 285 286 def tearDown(self): 287 pass 288 289 def test_principalFactory_interface(self): 290 verify.verifyClass(IAuthenticatedPrincipalFactory, 291 AuthenticatedApplicantPrincipalFactory 292 ) 293 return 294 295 def test_principalFactory_create(self): 296 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 297 298 assert factory.info is self.info 299 assert factory.request is None 300 return 301 302 def test_principalFactory_call_w_prefix(self): 303 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 304 principal = factory(FakePluggableAuth()) 305 306 assert isinstance(principal, ApplicantPrincipal) 307 self.assertEqual(principal.__repr__(), 308 "ApplicantPrincipal('foo.APP-1-1234567890')") 309 self.assertEqual(principal.id, 'foo.APP-1-1234567890') 310 return 311 312 def test_principalFactory_call_wo_prefix(self): 313 factory = AuthenticatedApplicantPrincipalFactory(self.info, None) 314 fake_auth = FakePluggableAuth() 315 fake_auth.prefix = None 316 principal = factory(fake_auth) 317 self.assertEqual(principal.id, 'APP-1-1234567890') 318 return 319 320 class PAUSetupTest(FunctionalTestCase): 321 # Check correct setup of authentication components in the 322 # applicants subpackage. 323 324 # When a university is created, we want by default have our local 325 # authentication components (an authenticator plugin and a 326 # credentials plugin) to be registered with the local PAU. Admins 327 # can remove these components on the fly later-on if they wish. 174 class FunctionalApplicantAuthTests(ApplicantsFullSetup): 328 175 329 176 layer = FunctionalLayer 330 177 331 178 def setUp(self): 332 super(PAUSetupTest, self).setUp() 333 334 # Setup a sample site for each test 335 app = University() 336 self.dc_root = tempfile.mkdtemp() 337 app['datacenter'].setStoragePath(self.dc_root) 338 339 # Prepopulate the ZODB... 340 self.getRootFolder()['app'] = app 341 self.app = self.getRootFolder()['app'] 342 self.browser = Browser() 343 self.browser.handleErrors = False 179 super(FunctionalApplicantAuthTests, self).setUp() 180 return 344 181 345 182 def tearDown(self): 346 super(PAUSetupTest, self).tearDown() 347 shutil.rmtree(self.dc_root) 348 349 def test_extra_auth_plugins_installed(self): 350 # Check whether the auth plugins defined in here are setup 351 # automatically when a university is created 352 353 # Get the PAU responsible for the local site ('app') 354 pau = getUtility(IAuthentication, context=self.app) 355 cred_plugins = pau.getCredentialsPlugins() 356 auth_plugins = pau.getAuthenticatorPlugins() 357 cred_names = [name for name, plugin in cred_plugins] 358 auth_names = [name for name, plugin in auth_plugins] 359 360 # Make sure our local ApplicantsAuthenticatorPlugin is registered... 361 self.assertTrue('applicants' in auth_names) 362 # Make sure our local WAeUPApplicantCredentialsPlugin is registered... 363 self.assertTrue('applicant_credentials' in cred_names) 183 super(FunctionalApplicantAuthTests, self).tearDown() 364 184 return 365 366 class FakePAU(object):367 credentialsPlugins = ()368 authenticatorPlugins = ()369 370 class ApplicantsAuthenticatorSetupTests(FunctionalTestCase):371 372 layer = FunctionalLayer373 374 def test_ifaces(self):375 # make sure we fullfill the interface promises376 obj = ApplicantsAuthenticatorSetup()377 verify.verifyClass(IAuthPluginUtility, ApplicantsAuthenticatorSetup)378 verify.verifyObject(IAuthPluginUtility, obj)379 return380 381 def test_utility_available(self):382 # we can get an applicant auth utility by lookup383 util = queryUtility(IAuthPluginUtility,384 name='applicants_auth_setup')385 self.assertTrue(util is not None)386 return387 388 def test_register(self):389 # make sure we can register additional components390 pau = FakePAU()391 result = ApplicantsAuthenticatorSetup().register(pau)392 self.assertEqual(393 result.credentialsPlugins, ('applicant_credentials',))394 self.assertEqual(395 result.authenticatorPlugins, ('applicants',))396 return397 398 def test_unregister(self):399 # make sure we can unregister applicant auth components400 pau = FakePAU()401 util = ApplicantsAuthenticatorSetup()402 pau = util.register(pau)403 result = util.unregister(pau)404 self.assertEqual(405 result.credentialsPlugins, ())406 self.assertEqual(407 result.authenticatorPlugins, ())408 return -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py
r7226 r7240 33 33 from waeup.sirp.applicants.container import ApplicantsContainer 34 34 from waeup.sirp.applicants.applicant import Applicant 35 from waeup.sirp.interfaces import IExtFileStore, IFileStoreNameChooser 35 from waeup.sirp.interfaces import ( 36 IExtFileStore, IFileStoreNameChooser, IUserAccount) 36 37 from waeup.sirp.university.faculty import Faculty 37 38 from waeup.sirp.university.department import Department … … 68 69 setSite(app) 69 70 71 self.login_path = 'http://localhost/app/login' 70 72 self.root_path = 'http://localhost/app/applicants' 71 73 self.manage_root_path = self.root_path + '/@@manage' … … 77 79 applicantscontainer = ApplicantsContainer() 78 80 applicantscontainer.ac_prefix = 'APP' 81 applicantscontainer.code = u'app2009' 79 82 applicantscontainer.prefix = 'app' 80 83 applicantscontainer.year = 2009 … … 84 87 applicantscontainer.enddate = date.today() + delta 85 88 self.app['applicants']['app2009'] = applicantscontainer 89 self.applicantscontainer = self.app['applicants']['app2009'] 86 90 87 91 # Populate university … … 112 116 113 117 # Add an applicant 114 self.applicant = Applicant() 115 self.pin_applicant = unicode(self.pins[1]) 116 self.applicant.access_code = self.pin_applicant 117 app['applicants']['app2009'][self.pin_applicant] = self.applicant 118 self.applicant = Applicant(container=applicantscontainer) 119 app['applicants']['app2009'][ 120 self.applicant.application_number] = self.applicant 121 IUserAccount( 122 self.app['applicants']['app2009'][ 123 self.applicant.application_number]).setPassword('apwd') 124 self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % ( 125 'app2009', self.applicant.application_number, 'manage') 126 self.edit_path = 'http://localhost/app/applicants/%s/%s/%s' % ( 127 'app2009', self.applicant.application_number, 'edit') 128 self.view_path = 'http://localhost/app/applicants/%s/%s' % ( 129 'app2009', self.applicant.application_number) 130 131 def login(self): 132 # Perform an applicant login. This creates an applicant record. 133 # 134 # This helper also sets `self.applicant`, which is the 135 # applicant object created. 136 self.browser.open(self.login_path) 137 self.browser.getControl(name="form.login").value = self.applicant.applicant_id 138 self.browser.getControl(name="form.password").value = 'apwd' 139 self.browser.getControl("Login").click() 140 141 def fill_correct_values(self): 142 # Fill the edit form with suitable values 143 self.browser.getControl(name="form.firstname").value = 'John' 144 self.browser.getControl(name="form.lastname").value = 'Tester' 145 self.browser.getControl(name="form.course1").value = ['CERT1'] 146 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 147 self.browser.getControl(name="form.lga").value = ['foreigner'] 148 self.browser.getControl(name="form.sex").value = ['m'] 149 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 118 150 119 151 def tearDown(self): … … 253 285 self.assertEqual(self.browser.headers['Status'], '200 Ok') 254 286 self.assertEqual(self.browser.url, self.add_applicant_path) 255 self.browser.getControl(name="form.ac_series").value = self.existing_series 256 self.browser.getControl(name="form.ac_number").value = self.existing_number 287 self.browser.getControl(name="form.firstname").value = 'Albert' 288 self.browser.getControl(name="form.lastname").value = 'Einstein' 289 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 257 290 self.browser.getControl("Create application record").click() 258 291 self.assertTrue('Application initialized' in self.browser.contents) 259 self.browser.open(self.add_applicant_path)260 self.browser.getControl(name="form.ac_series").value = '123'261 self.browser.getControl(name="form.ac_number").value = '456'262 self.browser.getControl("Create application record").click()263 self.assertTrue('is not a valid access code' in self.browser.contents)264 292 self.browser.open(self.container_manage_path) 265 293 self.assertEqual(self.browser.headers['Status'], '200 Ok') 266 294 ctrl = self.browser.getControl(name='val_id') 267 ctrl.getControl(value=self.existing_pin).selected = True 295 value = ctrl.options[0] 296 ctrl.getControl(value=value).selected = True 268 297 self.browser.getControl("Remove selected", index=0).click() 269 298 self.assertTrue('Successfully removed:' in self.browser.contents) 270 299 self.browser.open(self.add_applicant_path) 271 existing_pin = self.pins[2] 272 parts = existing_pin.split('-')[1:] 273 existing_series, existing_number = parts 274 self.browser.getControl(name="form.ac_series").value = existing_series 275 self.browser.getControl(name="form.ac_number").value = existing_number 300 self.browser.getControl(name="form.firstname").value = 'Albert' 301 self.browser.getControl(name="form.lastname").value = 'Einstein' 302 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 276 303 self.browser.getControl("Create application record").click() 277 304 self.assertTrue('Application initialized' in self.browser.contents) 278 self.browser.open(self.container_manage_path)279 self.assertTrue(existing_pin in self.browser.contents)280 del self.app['applicants']['app2009'][existing_pin]281 ctrl = self.browser.getControl(name='val_id')282 ctrl.getControl(value=existing_pin).selected = True283 self.browser.getControl("Remove selected", index=0).click()284 self.assertMatches('...Could not delete...', self.browser.contents)285 305 return 286 306 … … 288 308 # Managers can manage applicants 289 309 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 290 self.applicant_path = 'http://localhost/app/applicants/app2009/%s' % self.pin_applicant 291 self.applicant_view_path = self.applicant_path + '/index' 292 self.applicant_manage_path = self.applicant_path + '/manage' 293 self.applicant_slip_path = self.applicant_path + '/application_slip.pdf' 294 self.browser.open(self.applicant_manage_path) 295 self.assertEqual(self.browser.headers['Status'], '200 Ok') 296 self.browser.getControl(name="form.email").value = 'abc' 297 self.browser.getControl("Save").click() 298 self.assertMatches('...Invalid email address...', self.browser.contents) 299 self.browser.getControl(name="form.email").value = 'abc@def.gh' 300 self.browser.getControl(name="form.firstname").value = 'John' 301 self.browser.getControl(name="form.lastname").value = 'Tester' 302 self.browser.getControl(name="form.course1").value = ['CERT1'] 303 self.browser.getControl(name="form.course_admitted").value = ['CERT1'] 304 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 305 self.browser.getControl(name="form.lga").value = ['foreigner'] 306 self.browser.getControl(name="form.sex").value = ['m'] 310 self.slip_path = self.view_path + '/application_slip.pdf' 311 self.browser.open(self.manage_path) 312 self.assertEqual(self.browser.headers['Status'], '200 Ok') 313 self.fill_correct_values() 307 314 self.browser.getControl(name="transition").value = ['start'] 308 315 self.browser.getControl("Save").click() 309 316 self.assertMatches('...Form has been saved...', self.browser.contents) 310 317 self.assertMatches('...Application started by zope.mgr...', self.browser.contents) 311 self.browser.open(self. applicant_view_path)312 self.assertEqual(self.browser.headers['Status'], '200 Ok') 313 self.browser.open(self. applicant_slip_path)318 self.browser.open(self.view_path) 319 self.assertEqual(self.browser.headers['Status'], '200 Ok') 320 self.browser.open(self.slip_path) 314 321 self.assertEqual(self.browser.headers['Status'], '200 Ok') 315 322 self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') 316 self.browser.open(self. applicant_manage_path)323 self.browser.open(self.manage_path) 317 324 self.browser.getControl(name="form.course_admitted").value = [] 318 325 self.browser.getControl("Save").click() 319 self.browser.open(self. applicant_slip_path)326 self.browser.open(self.slip_path) 320 327 self.assertEqual(self.browser.headers['Status'], '200 Ok') 321 328 self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') 322 return323 324 def test_view_applicant(self):325 # Applicants can login and view their application326 self.login_path = 'http://localhost/app/applicants/app2009/login'327 self.browser.open(self.login_path)328 pin = self.pins[2]329 parts = pin.split('-')[1:]330 existing_series, existing_number = parts331 ac_series = self.browser.getControl(name="form.ac_series")332 ac_series.value = existing_series333 ac_number = self.browser.getControl(name="form.ac_number")334 ac_number.value = existing_number335 self.browser.getControl(name="SUBMIT").click()336 self.assertTrue(self.browser.url != self.login_path)337 self.assertEqual(self.browser.headers['Status'], '200 Ok')338 329 return 339 330 340 331 def test_passport_edit_view(self): 341 332 # We get a default image after login 342 login_path = 'http://localhost/app/applicants/app2009/login' 343 self.browser.open(login_path) 344 pin = self.pins[2] 345 parts = pin.split('-')[1:] 346 existing_series, existing_number = parts 347 ac_series = self.browser.getControl(name="form.ac_series") 348 ac_series.value = existing_series 349 ac_number = self.browser.getControl(name="form.ac_number") 350 ac_number.value = existing_number 351 self.browser.getControl(name="SUBMIT").click() 352 pin = self.pins[2] 353 #appl = self.getRootFolder()['app']['applicants']['app2009'] 354 #appl = appl[pin] 355 #passp = appl.passport 356 #passp_len = len(passp.file.read()) 357 #self.assertEqual(passp_len, PH_LEN) 358 #image_url = "%s/%s" % (self.browser.url, 'placeholder.jpg') 359 image_url = "%s/%s" % (self.browser.url, 'passport.jpg') 360 #self.browser.open(image_url) 361 self.browser.open('passport.jpg') 333 self.browser.open(self.login_path) 334 self.login() 335 self.browser.open(self.browser.url + '/passport.jpg') 362 336 self.assertEqual(self.browser.headers['status'], '200 Ok') 363 337 self.assertEqual(self.browser.headers['content-type'], 'image/jpeg') … … 366 340 self.browser.headers['content-length'], str(PH_LEN)) 367 341 368 369 342 def test_edit_applicant(self): 370 343 # Applicants can edit their record 371 self.login_path = 'http://localhost/app/applicants/app2009/login'372 344 self.browser.open(self.login_path) 373 pin = self.pins[2] 374 parts = pin.split('-')[1:] 375 existing_series, existing_number = parts 376 ac_series = self.browser.getControl(name="form.ac_series") 377 ac_series.value = existing_series 378 ac_number = self.browser.getControl(name="form.ac_number") 379 ac_number.value = existing_number 380 self.browser.getControl(name="SUBMIT").click() 345 self.login() 346 self.browser.open(self.edit_path) 381 347 self.assertTrue(self.browser.url != self.login_path) 382 348 self.assertEqual(self.browser.headers['Status'], '200 Ok') 383 self.browser.getControl(name="form.firstname").value = 'John' 384 self.browser.getControl(name="form.lastname").value = 'Tester' 385 self.browser.getControl(name="form.course1").value = ['CERT1'] 386 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988' 387 self.browser.getControl(name="form.lga").value = ['foreigner'] 388 self.browser.getControl(name="form.sex").value = ['m'] 349 self.fill_correct_values() 389 350 self.browser.getControl("Save").click() 390 351 self.assertMatches('...Form has been saved...', self.browser.contents) … … 421 382 return 422 383 423 # We have no local roles yet424 #def test_local_roles_add_delete(self):425 # # Managers can assign and delete local roles of applicants containers426 # myusers = self.app['users']427 # myusers.addUser('bob', 'bobssecret')428 # self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')429 # self.browser.open(self.manage_container_path)430 # self.browser.getControl(name="user").value = ['bob']431 # self.browser.getControl(name="local_role").value = [432 # 'waeup.local.ApplicationsOfficer']433 # self.browser.getControl("Add local role").click()434 # self.assertTrue('<td>bob</td>' in self.browser.contents)435 # ctrl = self.browser.getControl(name='role_id')436 # ctrl.getControl(value='bob|waeup.ApplicationsOfficer').selected = True437 # self.browser.getControl("Remove selected local roles").click()438 # self.assertTrue('Successfully removed:' in self.browser.contents)439 # self.assertFalse('<td>bob</td>' in self.browser.contents)440 # return441 442 class LoginTest(FunctionalTestCase):443 # Here we check login view of applicants containers.444 #445 # Tests in here do only cover login attempts without any PINs446 # created before.447 448 layer = FunctionalLayer449 450 def setUp(self):451 super(LoginTest, self).setUp()452 453 # Setup a sample site for each test454 app = University()455 self.dc_root = tempfile.mkdtemp()456 app['datacenter'].setStoragePath(self.dc_root)457 self.login_path = 'http://localhost/app/applicants/testapplicants/login'458 459 # Add an applicants container where we can login (or not)460 applicantscontainer = ApplicantsContainer()461 applicantscontainer.ac_prefix = 'APP'462 delta = timedelta(days=10)463 applicantscontainer.startdate = date.today() - delta464 applicantscontainer.enddate = date.today() + delta465 app['applicants']['testapplicants'] = applicantscontainer466 467 # Put the prepopulated site into test ZODB and prepare test468 # browser469 self.getRootFolder()['app'] = app470 self.browser = Browser()471 self.browser.handleErrors = False472 473 def tearDown(self):474 super(LoginTest, self).tearDown()475 shutil.rmtree(self.dc_root)476 477 def test_anonymous_access(self):478 # Anonymous users can access a login page479 self.browser.open(self.login_path)480 self.assertEqual(self.browser.headers['Status'], '200 Ok')481 return482 483 def test_anonymous_invalid_creds(self):484 # Anonymous users giving invalid credentials stay at the page485 self.browser.open(self.login_path)486 # We do not give credentials but send the form as-is487 submit = self.browser.getControl(name='SUBMIT')488 submit.click()489 # We are still at the same page...490 self.assertEqual(self.browser.url, self.login_path)491 self.assertEqual(self.browser.headers['Status'], '200 Ok')492 return493 494 def test_anonymous_invalid_creds_warning(self):495 # Entering wrong credentials will yield a warning496 self.browser.open(self.login_path)497 # We do not give credentials but send the form as-is498 submit = self.browser.getControl(name='SUBMIT')499 submit.click()500 self.assertTrue(501 'Entered credentials are invalid' in self.browser.contents)502 return503 504 def test_manager_no_warnings(self):505 # Browsing the login screen as a manager, won't raise warnings506 # Authenticate ourself as manager507 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')508 self.browser.open(self.login_path)509 # Submit the form w/o any credentials510 self.browser.getControl(name="SUBMIT").click()511 self.assertTrue(512 'Entered credentials are invalid' not in self.browser.contents)513 return514 515 def test_manager_no_redirect(self):516 # Browsing the login screen as a manager won't trigger a redirect517 # Authenticate ourself as manager518 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')519 self.browser.open(self.login_path)520 # Submit the form w/o any credentials521 self.browser.getControl(name="SUBMIT").click()522 self.assertEqual(self.browser.url, self.login_path)523 return524 525 def test_display_entered_values(self):526 # After submit the entered values are displayed in the form527 self.browser.open(self.login_path)528 # Enter some value we can look for after submit529 ac_series = self.browser.getControl(name="form.ac_series")530 ac_series.value = '666'531 self.browser.getControl(name="SUBMIT").click()532 self.assertTrue('666' in self.browser.contents)533 return534 535 class LoginTestWithPINs(LoginTest):536 # Here we check login view of applicants containers with PINs provided.537 538 # As setting up pins is time-consuming we only set them up when539 # really needed (i.e. in this separate TestCase).540 541 layer = FunctionalLayer542 543 def setUp(self):544 super(LoginTestWithPINs, self).setUp()545 546 # Create 5 access codes with prefix'FOO' and cost 9.99 each547 pin_container = self.getRootFolder()['app']['accesscodes']548 pin_container.createBatch(549 datetime.now(), 'some_userid', 'APP', 9.99, 5)550 pins = pin_container[pin_container.keys()[0]].values()551 self.pins = [x.representation for x in pins]552 self.existing_pin = self.pins[0]553 parts = self.existing_pin.split('-')[1:]554 self.existing_series, self.existing_number = parts555 self.browser.handleErrors = False556 557 def tearDown(self):558 super(LoginTestWithPINs, self).tearDown()559 560 def test_anonymous_valid_login(self):561 # If we enter valid credentials, we get to the applicants form562 self.browser.open(self.login_path)563 # Enter some value we can look for after submit564 ac_series = self.browser.getControl(name="form.ac_series")565 ac_series.value = self.existing_series566 ac_number = self.browser.getControl(name="form.ac_number")567 ac_number.value = self.existing_number568 self.browser.getControl(name="SUBMIT").click()569 # We should be redirected to applicants form.570 self.assertTrue(self.browser.url != self.login_path)571 # Applicants see their Access Code in the contact form572 #self.browser.getLink("Contact").click()573 #self.assertTrue(574 # 'Access Code:' in self.browser.contents)575 return576 577 def test_anonymous_invalid_login(self):578 # If we enter wrong credentials we won't get far579 self.browser.open(self.login_path)580 # Enter some value we can look for after submit581 ac_series = self.browser.getControl(name="form.ac_series")582 ac_series.value = 'illegal series'583 ac_number = self.browser.getControl(name="form.ac_number")584 ac_number.value = 'invalid number'585 self.browser.getControl(name="SUBMIT").click()586 # We get a warning message587 self.assertTrue(588 'Entered credentials are invalid' in self.browser.contents)589 # We stay at the login page (no redirect)590 self.assertTrue(self.browser.url == self.login_path)591 return592 593 384 class ApplicantsPassportTests(ApplicantsFullSetup): 594 385 # Tests for uploading/browsing the passport image of appplicants 595 386 596 387 layer = FunctionalLayer 597 598 def setUp(self):599 super(ApplicantsPassportTests, self).setUp()600 self.login_path = 'http://localhost/app/applicants/app2009/login'601 self.pin = self.pins[2]602 self.existing_series, self.existing_number = self.pin.split('-')[1:]603 self.edit_path = 'http://localhost/app/applicants/app2009/%s/edit' % (604 self.pin)605 self.manage_path = 'http://localhost/app/applicants/%s/%s/%s' % (606 'app2009', self.pin, 'manage')607 608 def tearDown(self):609 super(ApplicantsPassportTests, self).tearDown()610 611 def login(self):612 # Perform an applicant login. This creates an applicant record.613 #614 # This helper also sets `self.applicant`, which is the615 # applicant object created.616 self.browser.open(self.login_path)617 ac_series = self.browser.getControl(name="form.ac_series")618 ac_series.value = self.existing_series619 ac_number = self.browser.getControl(name="form.ac_number")620 ac_number.value = self.existing_number621 self.browser.getControl(name="SUBMIT").click()622 self.applicant = self.app['applicants']['app2009'][self.pin]623 624 def fill_correct_values(self):625 # Fill the edit form with suitable values626 self.browser.getControl(name="form.firstname").value = 'John'627 self.browser.getControl(name="form.lastname").value = 'Tester'628 self.browser.getControl(name="form.course1").value = ['CERT1']629 self.browser.getControl(name="form.date_of_birth").value = '09/09/1988'630 self.browser.getControl(name="form.lga").value = ['foreigner']631 self.browser.getControl(name="form.sex").value = ['m']632 388 633 389 def image_url(self, filename): … … 638 394 #import pdb; pdb.set_trace() 639 395 self.login() 640 self.assertEqual(self.browser.url, self.edit_path) 396 self.assertEqual(self.browser.url, self.view_path) 397 self.browser.open(self.edit_path) 398 # There is a correct <img> link included 399 self.assertTrue( 400 '<img src="passport.jpg" />' in self.browser.contents) 401 # Browsing the link shows a real image 402 self.browser.open(self.image_url('passport.jpg')) 403 self.assertEqual( 404 self.browser.headers['content-type'], 'image/jpeg') 405 self.assertEqual(len(self.browser.contents), PH_LEN) 406 407 def test_after_submit_default_browsable(self): 408 # After submitting an applicant form the default image is 409 # still visible 410 self.login() 411 self.browser.open(self.edit_path) 412 self.browser.getControl("Save").click() # submit form 641 413 # There is a correct <img> link included 642 414 self.assertTrue( … … 648 420 self.assertEqual(len(self.browser.contents), PH_LEN) 649 421 650 def test_after_submit_default_browsable(self):651 # After submitting an applicant form the default image is652 # still visible653 self.login()654 self.browser.getControl("Save").click() # submit form655 # There is a correct <img> link included656 self.assertTrue(657 '<img src="passport.jpg" />' in self.browser.contents)658 # Browsing the link shows a real image659 self.browser.open(self.image_url('passport.jpg'))660 self.assertEqual(661 self.browser.headers['content-type'], 'image/jpeg')662 self.assertEqual(len(self.browser.contents), PH_LEN)663 664 422 def test_uploaded_image_respects_file_size_restriction(self): 665 423 # When we upload an image that is too big ( > 10 KB) we will 666 424 # get an error message 667 425 self.login() 426 self.browser.open(self.edit_path) 668 427 # Create a pseudo image file and select it to be uploaded in form 669 428 photo_content = 'A' * 1024 * 21 # A string of 21 KB size … … 694 453 # even if there are still errors in the form 695 454 self.login() 455 self.browser.open(self.edit_path) 696 456 # Create a pseudo image file and select it to be uploaded in form 697 457 photo_content = 'I pretend to be a graphics file' … … 714 474 # stored in an imagestorage 715 475 self.login() 476 self.browser.open(self.edit_path) 716 477 # Create a pseudo image file and select it to be uploaded in form 717 478 pseudo_image = StringIO('I pretend to be a graphics file') … … 731 492 # if there are no errors in form 732 493 self.login() 494 self.browser.open(self.edit_path) 733 495 self.fill_correct_values() # fill other fields with correct values 734 496 # Create a pseudo image file and select it to be uploaded in form … … 751 513 # stored in an imagestorage if form contains no errors 752 514 self.login() 515 self.browser.open(self.edit_path) 753 516 self.fill_correct_values() # fill other fields with correct values 754 517 # Create a pseudo image file and select it to be uploaded in form … … 768 531 # Make sure uploaded images do really differ if we eject a 769 532 # change notfication (and do not if we don't) 770 self.login() # Create applicant form 533 self.login() 534 self.browser.open(self.edit_path) 771 535 self.fill_correct_values() # fill other fields with correct values 772 536 self.browser.getControl("Save").click() # submit form … … 795 559 # Make sure that a correctly filled form with passport picture 796 560 # can be submitted 797 self.login() # Create applicant form 561 self.login() 562 self.browser.getLink("Edit application record").click() 798 563 self.fill_correct_values() # fill other fields with correct values 799 564 # Create a pseudo image file and select it to be uploaded in form … … 814 579 # Now do the whole thing again but with correct state 815 580 self.login() 581 self.browser.open(self.edit_path) 816 582 self.fill_correct_values() 817 583 pseudo_image = StringIO('I pretend to be a graphics file') … … 836 602 def test_locking(self): 837 603 # Make sure that locked forms can't be submitted 838 self.login() # Create applicant form 604 self.login() 605 self.browser.open(self.edit_path) 839 606 self.fill_correct_values() # fill other fields with correct values 840 607 # Create a pseudo image file and select it to be uploaded in form … … 844 611 file_ctrl.add_file(pseudo_image, filename='myphoto.jpg') 845 612 self.browser.getControl("Save").click() 846 self.browser.getLink("Logout").click() 847 848 # Login as manager and lock the form 849 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 850 self.browser.open(self.manage_path) 851 self.browser.getControl(name="form.locked").value = True 852 self.browser.getControl("Save").click() 853 self.browser.getLink("Logout").click() 854 855 # Login as applicant again and try to open the edit form 856 self.login() 613 # Now we lock the form 614 self.applicant.locked = True 857 615 self.browser.open(self.edit_path) 858 616 self.assertEqual(self.browser.headers['Status'], '200 Ok') -
main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_catalog.py
r7193 r7240 58 58 # Create an applicant in an applicants container 59 59 self.container = ApplicantsContainer() 60 self.container.code = u"mystuff" 60 61 setSite(self.app) 61 62 self.app['applicants']['mystuff'] = self.container 62 self.applicant = Applicant() 63 self.ac = u'FOO-666-123456789' 64 self.applicant.access_code = self.ac 65 self.app['applicants']['mystuff'][self.ac] = self.applicant 63 self.applicant = Applicant(container=self.container) 64 self.app['applicants']['mystuff'][ 65 self.applicant.application_number] = self.applicant 66 66 return 67 67 … … 80 80 self.create_applicant() 81 81 q = getUtility(IQuery) 82 subquery = Eq(('applicants_catalog', 'access_code'), self.ac) 82 subquery = Eq(('applicants_catalog', 'applicant_id'), 83 self.applicant.applicant_id) 83 84 results = list(q.searchResults(subquery)) 84 85 self.assertEqual(len(results), 1) 85 86 86 result_applicant = results[0] 87 87 self.assertTrue(isinstance(result_applicant, Applicant)) 88 self.assertEqual(result_applicant.a ccess_code, self.ac)88 self.assertEqual(result_applicant.applicant_id, self.applicant.applicant_id)
Note: See TracChangeset for help on using the changeset viewer.