def new_diagnostic_vars(user: user_pb2.User, **unused_kwargs: Any) -> Dict[str, str]: """Compute vars for the "New Diagnostic".""" frustrations_set = set(user.profile.frustrations) frustrations_vars = { f'frustration_{name}': campaign.as_template_boolean(key in frustrations_set) for name, key in user_pb2.Frustration.items() } age = datetime.date.today().year - user.profile.year_of_birth has_children = user.profile.family_situation in { user_pb2.FAMILY_WITH_KIDS, user_pb2.SINGLE_PARENT_SITUATION, } survey_token = parse.quote( auth.create_token(user.user_id, role='employment-status')) auth_token = parse.quote( auth.create_token(user.user_id, is_using_timestamp=True)) redirect_url = f'{campaign.BASE_URL}/statut/ne-recherche-plus' return dict( dict(frustrations_vars, **campaign.get_default_vars(user)), **{ 'mayHaveSeekingChildren': campaign.as_template_boolean(has_children and age >= 45), 'loginUrl': f'{campaign.BASE_URL}?userId={user.user_id}&authToken={auth_token}', 'stopSeekingUrl': f'{campaign.BASE_URL}/api/employment-status?user={user.user_id}&token={survey_token}&' f'seeking=STOP_SEEKING&redirect={parse.quote(redirect_url)}', })
def employment_vars(user, unused_db=None): """Compute vars for a given user for the employment survey. Returns: a dict with all vars required for the template, or None if no email should be sent. """ registered_months_ago = campaign.get_french_months_ago( user.registered_at.ToDatetime()) if not registered_months_ago: logging.warning('User registered only recently (%s)', user.registered_at) return None # If the users have already updated their employment status less than one month ago, # ignore them. for status in user.employment_status: if status.created_at.ToDatetime() > _ONE_MONTH_AGO: return None survey_token = parse.quote( auth.create_token(user.user_id, role='employment-status')) unsubscribe_token = parse.quote( auth.create_token(user.profile.email, role='unsubscribe')) return { 'firstName': french.cleanup_firstname(user.profile.name), 'registeredMonthsAgo': registered_months_ago, 'seekingUrl': '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'. format( campaign.BASE_URL, user.user_id, survey_token, 'STILL_SEEKING', parse.quote('{}/statut/en-recherche'.format(campaign.BASE_URL)), ), 'stopSeekingUrl': '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'. format( campaign.BASE_URL, user.user_id, survey_token, 'STOP_SEEKING', parse.quote('{}/statut/ne-recherche-plus'.format( campaign.BASE_URL)), ), 'unsubscribeLink': '{}/unsubscribe.html?email={}&auth={}'.format( campaign.BASE_URL, parse.quote(user.profile.email), unsubscribe_token), }
def test_create_token(self) -> None: """Basic usage of create_token.""" token_1 = auth.create_token('*****@*****.**', 'login') self.assertTrue(token_1) token_2 = auth.create_token('*****@*****.**', 'unsubscribe') self.assertTrue(token_2) self.assertNotEqual(token_1, token_2) token_3 = auth.create_token('*****@*****.**', 'login') self.assertTrue(token_3) self.assertNotEqual(token_1, token_3)
def _get_improve_cv_vars( user: user_pb2.User, now: datetime.datetime, **unused_kwargs: Any) -> Optional[Dict[str, Any]]: """Compute vars for the "Improve your CV" email.""" if user_pb2.RESUME not in user.profile.frustrations: logging.info('User is not frustrated by its CV') return None project = user.projects[0] if project.kind == project_pb2.FIND_A_FIRST_JOB: has_experience = 'False' elif project.kind in (project_pb2.FIND_A_NEW_JOB, project_pb2.FIND_ANOTHER_JOB): has_experience = 'True' else: has_experience = '' deep_link_advice_url = \ campaign.get_deep_link_advice(user.user_id, project, 'improve-resume') or \ campaign.get_deep_link_advice(user.user_id, project, 'fresh-resume') auth_token = parse.quote(auth.create_token(user.user_id, is_using_timestamp=True)) return dict(campaign.get_default_coaching_email_vars(user), **{ 'deepLinkAdviceUrl': deep_link_advice_url, 'hasExperience': has_experience, 'isSeptember': campaign.as_template_boolean(now.month == 9), 'loginUrl': f'{campaign.BASE_URL}?userId={user.user_id}&authToken={auth_token}', })
def test_auth_user_id_token(self, mock_time: mock.MagicMock) -> None: """Authenticate using the user ID and a token.""" now = time.time() mock_time.time.return_value = now # Create password. user_id = self.authenticate_new_user(email='*****@*****.**', password='******', first_name='Pascal', last_name='Corpet') timed_token = auth.create_token(user_id, is_using_timestamp=True) # 2 days later… mock_time.time.return_value = now + 86400 * 2 response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "{user_id}", "authToken": "{timed_token}"}}', content_type='application/json') auth_response = self.json_from_response(response) self.assertEqual( 'Pascal', auth_response.get('authenticatedUser', {}).get('profile', {}).get('name')) self.assertTrue(auth_response.get('lastAccessAt'))
def test_update_create_new_employment_status(self, mock_now): """Create a new employment status when update is a day later.""" mock_now.return_value = datetime.datetime.now() user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': 'STOP_SEEKING', 'redirect': 'http://www.tutut.org', }) # Waiting 36 hours before updating the status: we then create a new one. mock_now.return_value = datetime.datetime.now() + datetime.timedelta(hours=36) response = self.app.post( '/api/employment-status/{}'.format(user_id), data='{"seeking": "STILL_SEEKING", "bobHasHelped": "YES_A_LOT"}', headers={'Authorization': 'Bearer ' + survey_token}, content_type='application/json') self.assertEqual(200, response.status_code) user = self.get_user_info(user_id, auth_token) self.assertEqual( ['STOP_SEEKING', 'STILL_SEEKING'], [s.get('seeking') for s in user.get('employmentStatus', [])])
def _employment_vars( user: user_pb2.User, now: datetime.datetime, **unused_kwargs: Any) \ -> Optional[Dict[str, str]]: """Compute vars for a given user for the employment survey. Returns: a dict with all vars required for the template, or None if no email should be sent. """ registered_months_ago = campaign.get_french_months_ago( user.registered_at.ToDatetime(), now=now) if not registered_months_ago: logging.warning('User registered only recently (%s)', user.registered_at) return None # If the users have already updated their employment status less than one month ago, # ignore them. for status in user.employment_status: if status.created_at.ToDatetime() > _ONE_MONTH_AGO: return None survey_token = parse.quote( auth.create_token(user.user_id, role='employment-status')) redirect_url = parse.quote(f'{campaign.BASE_URL}/statut/en-recherche') return dict( campaign.get_default_vars(user), **{ 'registeredMonthsAgo': registered_months_ago, 'seekingUrl': f'{campaign.BASE_URL}/api/employment-status?user={user.user_id}&token={survey_token}&' f'seeking=STILL_SEEKING&redirect={redirect_url}', 'stopSeekingUrl': f'{campaign.BASE_URL}/api/employment-status?user={user.user_id}&token={survey_token}&' f'seeking=STOP_SEEKING&redirect={redirect_url}', })
def test_auth_user_id_token_corrupted_data( self, mock_warning: mock.MagicMock) -> None: """Authenticate using the user ID and a token but data is corrupted.""" # Create password. user_id = self.authenticate_new_user(email='*****@*****.**', password='******', first_name='Pascal', last_name='Corpet') timed_token = auth.create_token(user_id, is_using_timestamp=True) # Screw the data in Mongo. self._user_db.user.update_one({}, {'$set': {'revision': {'hack': 1}}}) response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "{user_id}", "authToken": "{timed_token}"}}', content_type='application/json') self.assertEqual(500, response.status_code) self.assertIn('Les données utilisateur sont corrompues', response.get_data(as_text=True)) mock_warning.assert_called_once() self.assertIn( 'Failed to parse revision', mock_warning.call_args[0][0] % mock_warning.call_args[0][1:])
def test_employment_status_invalid_id(self): """EmploymentSurvey endpoint should not save anything if called with an invalid ID.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': '1', 'redirect': 'http://www.tutut.org' }) self.assertEqual(302, response.status_code) user = self.get_user_info(user_id, auth_token) redirect_args = dict(parse.parse_qsl(parse.urlparse(response.location).query)) self.assertIn('id', redirect_args) self.assertEqual(survey_token, redirect_args['token']) self.assertEqual(user_id, redirect_args['user']) self.assertEqual(redirect_args['id'], '0') self.assertEqual(user['employmentStatus'][0]['seeking'], 'STILL_SEEKING') survey_response = { 'situation': 'lalala', 'bobHasHelped': 'bidulechose' } response2 = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'id': int(redirect_args['id']) + 3, **survey_response }) self.assertEqual(422, response2.status_code) for key in survey_response: self.assertNotIn(key, user['employmentStatus'][0])
def test_auth_user_id_token_outdated(self, mock_time: mock.MagicMock) -> None: """Authenticate using the user ID and a very old token.""" now = time.time() mock_time.time.return_value = now # Create password. user_id = self.authenticate_new_user(email='*****@*****.**', password='******', first_name='Pascal', last_name='Corpet') timed_token = auth.create_token(user_id, is_using_timestamp=True) # 10 days later… mock_time.time.return_value = now + 86400 * 10 response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "{user_id}", "authToken": "{timed_token}"}}', content_type='application/json') self.assertEqual(498, response.status_code) self.assertIn("Token d'authentification périmé", response.get_data(as_text=True))
def _send_activation_email(user: user_pb2.User, project: project_pb2.Project, database: pymongo_database.Database, base_url: str) -> None: """Send an email to the user just after we have defined their diagnosis.""" if '@' not in user.profile.email: return # Set locale. locale.setlocale(locale.LC_ALL, 'fr_FR.UTF-8') scoring_project = scoring.ScoringProject(project, user, database, now=now.get()) auth_token = parse.quote( auth.create_token(user.user_id, is_using_timestamp=True)) settings_token = parse.quote( auth.create_token(user.user_id, role='settings')) coaching_email_frequency_name = \ user_pb2.EmailFrequency.Name(user.profile.coaching_email_frequency) data = { 'changeEmailSettingsUrl': f'{base_url}/unsubscribe.html?user={user.user_id}&auth={settings_token}&' f'coachingEmailFrequency={coaching_email_frequency_name}&' f'hl={parse.quote(user.profile.locale)}', 'date': now.get().strftime('%d %B %Y'), 'firstName': user.profile.name, 'gender': user_pb2.Gender.Name(user.profile.gender), 'isCoachingEnabled': 'True' if user.profile.coaching_email_frequency and user.profile.coaching_email_frequency != user_pb2.EMAIL_NONE else '', 'loginUrl': f'{base_url}?userId={user.user_id}&authToken={auth_token}', 'ofJob': scoring_project.populate_template('%ofJobName', raise_on_missing_var=True), } # https://app.mailjet.com/template/636862/build response = mail.send_template('636862', user.profile, data) if response.status_code != 200: logging.warning('Error while sending diagnostic email: %s\n%s', response.status_code, response.text)
def get_status_update_link(user_id: str, profile: user_pb2.UserProfile) -> str: """Make link with token from user ID for RER status update.""" survey_token = parse.quote(auth.create_token(user_id, role='employment-status')) # TODO(pascal): Drop can_tutoie when the RER page uses i18n. return f'{BASE_URL}/statut/mise-a-jour?user={user_id}&token={survey_token}&' \ f'gender={user_pb2.Gender.Name(profile.gender)}' + \ ('&can_tutoie=true' if profile.can_tutoie else '') + \ f'&hl={parse.quote(profile.locale)}'
def new_diagnostic_vars(user, unused_db=None): """Compute vars for the "New Diagnostic".""" unsubscribe_token = parse.quote( auth.create_token(user.profile.email, role='unsubscribe')) frustrations_vars = { 'frustration_{}'.format(user_pb2.Frustration.Name(f)): 'True' for f in user.profile.frustrations } age = datetime.date.today().year - user.profile.year_of_birth has_children = user.profile.family_situation in { user_pb2.FAMILY_WITH_KIDS, user_pb2.SINGLE_PARENT_SITUATION, } survey_token = parse.quote( auth.create_token(user.user_id, role='employment-status')) auth_token = parse.quote( auth.create_token(user.user_id, is_using_timestamp=True)) return dict( frustrations_vars, **{ 'firstName': french.cleanup_firstname(user.profile.name), 'gender': user_pb2.Gender.Name(user.profile.gender), 'mayHaveSeekingChildren': campaign.as_template_boolean(has_children and age >= 45), 'loginUrl': '{}?userId={}&authToken={}'.format(campaign.BASE_URL, user.user_id, auth_token), 'stopSeekingUrl': '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'. format( campaign.BASE_URL, user.user_id, survey_token, 'STOP_SEEKING', parse.quote('{}/statut/ne-recherche-plus'.format( campaign.BASE_URL)), ), 'unsubscribeLink': '{}/unsubscribe.html?email={}&auth={}'.format( campaign.BASE_URL, parse.quote(user.profile.email), unsubscribe_token), })
def test_employment_status_invalid_token(self): """EmploymentSurvey endpoint should fail if called with an invalid token.""" user_id = self.create_user(email='*****@*****.**') auth_token = auth.create_token(user_id, role='invalid-role') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': auth_token, 'seeking': '1', }) self.assertEqual(403, response.status_code)
def get_default_vars(user: user_pb2.User, **unused_kwargs: Any) -> Dict[str, str]: """Compute default variables used in all emails: firstName, gender and unsubscribeLink.""" unsubscribe_token = parse.quote(auth.create_token(user.profile.email, role='unsubscribe')) return { 'firstName': french.cleanup_firstname(user.profile.name), 'gender': user_pb2.Gender.Name(user.profile.gender), 'unsubscribeLink': f'{BASE_URL}/unsubscribe.html?user={parse.quote(user.user_id)}&' f'auth={unsubscribe_token}&hl={parse.quote(user.profile.locale)}', }
def test_auth_user_id_token_unknown_user(self) -> None: """Authenticate using a token but the user ID does not correspond to anything.""" user_id = str(objectid.ObjectId()) timed_token = auth.create_token(user_id, is_using_timestamp=True) response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "{user_id}", "authToken": "{timed_token}"}}', content_type='application/json') self.assertEqual(404, response.status_code) self.assertIn('Utilisateur inconnu', response.get_data(as_text=True))
def test_employment_status_seeking_wrong_string(self): """Test passing seeking parameter as string.""" user_id = self.create_user(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': 'ERRONEOUS', 'redirect': 'http://www.tutut.org', }) self.assertEqual(422, response.status_code)
def test_employment_status_seeking_string(self): """Test passing seeking parameter as string.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': 'STOP_SEEKING', 'redirect': 'http://www.tutut.org', }) self.assertEqual(302, response.status_code) user = self.get_user_info(user_id, auth_token) self.assertEqual(user['employmentStatus'][0]['seeking'], 'STOP_SEEKING')
def test_update_employment_status(self): """Update the employment status through the POST endpoint.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.post( '/api/employment-status/{}'.format(user_id), data='{"seeking": "STILL_SEEKING", "bobHasHelped": "YES_A_LOT"}', headers={'Authorization': 'Bearer ' + survey_token}, content_type='application/json') self.assertEqual(200, response.status_code) user = self.get_user_info(user_id, auth_token) self.assertEqual(['STILL_SEEKING'], [s.get('seeking') for s in user['employmentStatus']])
def generate_auth_tokens(user_id): r"""Generates auth token for a given user. Note that this is safe to do as long as the user had a proper and complete auth token which is ensured by the @auth.require_user above. The "easiest" way to use it: - open a the Chrome Console on Bob - run the following js commands: ``` (() => {const authToken = document.cookie.match(/(^|;\s*)authToken=(.*?)(\s*;|$)/)[2]; const userId = document.cookie.match(/(^|;\s*)userId=(.*?)(\s*;|$)/)[2]; fetch(`/api/user/${userId}/generate-auth-tokens`, { headers: {Authorization: `Bearer ${authToken}`}, }).then(response => response.json()).then(console.log)})() ``` """ user_data = _USER_DB.user.find_one({'_id': _safe_object_id(user_id)}, { 'profile.email': 1, '_id': 0 }) if not user_data: flask.abort(404, 'Utilisateur "{}" inconnu.'.format(user_id)) email = user_data.get('profile', {}).get('email') # TODO(cyrille): Add user to simplify use in url. return json.dumps({ 'auth': auth.create_token(user_id, is_using_timestamp=True), 'employment-status': auth.create_token(user_id, 'employment-status'), 'nps': auth.create_token(user_id, 'nps'), 'unsubscribe': auth.create_token(email, 'unsubscribe'), 'user': user_id, })
def body_language_vars(user, unused_db=None): """Compute vars for a given user for the body language email. Returns: a dict with all vars required for the template, or None if no email should be sent. """ if not user.projects: logging.info('User has no project') return None registered_months_ago = campaign.get_french_months_ago( user.registered_at.ToDatetime()) if not registered_months_ago: logging.info('User registered only recently (%s)', user.registered_at) return None has_read_last_focus_email = any(email.status in _READ_EMAIL_STATUSES for email in user.emails_sent if email.campaign_id.startswith('focus-')) worst_frustration = next( (user_pb2.Frustration.Name(frustration) for frustration in (user_pb2.SELF_CONFIDENCE, user_pb2.INTERVIEW, user_pb2.ATYPIC_PROFILE) if frustration in user.profile.frustrations), '') if not worst_frustration: return None unsubscribe_token = parse.quote( auth.create_token(user.profile.email, role='unsubscribe')) return { 'firstName': french.cleanup_firstname(user.profile.name), 'gender': user_pb2.Gender.Name(user.profile.gender), 'hasReadLastFocusEmail': campaign.as_template_boolean(has_read_last_focus_email), 'registeredMonthsAgo': registered_months_ago, 'unsubscribeLink': '{}/unsubscribe.html?email={}&auth={}'.format( campaign.BASE_URL, parse.quote(user.profile.email), unsubscribe_token), 'worstFrustration': worst_frustration, }
def test_missing_parameters(self): """EmploymentSurvey endpoint expect user and token parameters""" user_id = self.create_user(email='*****@*****.**') auth_token = auth.create_token(user_id, role='employment-survey') response = self.app.get('/api/employment-status', query_string={ 'token': auth_token, 'seeking': '1', }) self.assertEqual(422, response.status_code) response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'seeking': '1', }) self.assertEqual(422, response.status_code)
def get_default_coaching_email_vars(user: user_pb2.User, **unused_kwargs: Any) -> Dict[str, str]: """Compute default variables used in all coaching emails.""" settings_token = parse.quote(auth.create_token(user.user_id, role='settings')) return dict(get_default_vars(user), **{ 'changeEmailSettingsUrl': f'{BASE_URL}/unsubscribe.html?user={parse.quote(user.user_id)}&auth={settings_token}&' 'coachingEmailFrequency=' + user_pb2.EmailFrequency.Name(user.profile.coaching_email_frequency) + f'&hl={parse.quote(user.profile.locale)}', 'firstName': french.cleanup_firstname(user.profile.name), 'gender': user_pb2.Gender.Name(user.profile.gender), # TODO(pascal): Harmonize use of URL suffix (instead of link). 'statusUpdateUrl': get_status_update_link(user.user_id, user.profile), })
def test_employment_status_stop_seeking(self) -> None: """Test expected use case of employment-survey when user click on stop seeking.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': '2', 'redirect': 'http://www.tutut.org', }) self.assertEqual(302, response.status_code) user = self.get_user_info(user_id, auth_token) self.assertEqual(user['employmentStatus'][0]['seeking'], 'STOP_SEEKING')
def test_auth_user_id_token_wrong_format_id(self) -> None: """Authenticate using a token but a wrongly formatted user ID.""" # Create password. user_id = self.authenticate_new_user(email='*****@*****.**', password='******', first_name='Pascal', last_name='Corpet') timed_token = auth.create_token(user_id, is_using_timestamp=True) response = self.app.post( '/api/user/authenticate', data=f'{{"userId": "aaa", "authToken": "{timed_token}"}}', content_type='application/json') self.assertEqual(400, response.status_code) self.assertIn( "L'identifiant utilisateur "aaa" n'a pas le bon format", response.get_data(as_text=True))
def send_email_to_user(user, user_id, base_url): """Sends an email to the user to measure the Net Promoter Score.""" # Renew actions for the day if needed. mail_result = mail.send_template( _MAILJET_TEMPLATE_ID, user.profile, { 'baseUrl': base_url, 'firstName': french.cleanup_firstname(user.profile.name), 'npsFormUrl': '{}/api/nps?user={}&token={}&redirect={}'.format( base_url, user_id, auth.create_token(user_id, 'nps'), parse.quote('{}/retours'.format(base_url)), ), }, dry_run=DRY_RUN, ) mail_result.raise_for_status() return mail_result
def test_employment_status(self) -> None: """Test expected use case of employment-survey endpoints.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') response = self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': '1', 'redirect': 'http://www.tutut.org', }) self.assertEqual(302, response.status_code) user = self.get_user_info(user_id, auth_token) assert response.location redirect_args = dict( parse.parse_qsl(parse.urlparse(response.location).query)) self.assertIn('id', redirect_args) self.assertEqual(survey_token, redirect_args['token']) self.assertEqual(user_id, redirect_args['user']) self.assertEqual(user['employmentStatus'][0]['seeking'], 'STILL_SEEKING') survey_response = { 'situation': 'lalala', 'bobHasHelped': 'bidulechose' } response2 = self.app.post( f'/api/employment-status/{user_id}', data=json.dumps(survey_response), headers={'Authorization': 'Bearer ' + survey_token}, content_type='application/json') self.assertEqual(200, response2.status_code) user = self.get_user_info(user_id, auth_token) self.assertTrue(len(user['employmentStatus']) == 1) status = user['employmentStatus'][0] for key, value in survey_response.items(): self.assertEqual(status[key], value) # check other fields have not been lost. self.assertEqual(user['profile']['email'], '*****@*****.**')
def test_update_existing_employment_status(self): """Update an existing employment status through the POST endpoint.""" user_id, auth_token = self.create_user_with_token(email='*****@*****.**') survey_token = auth.create_token(user_id, role='employment-status') self.app.get('/api/employment-status', query_string={ 'user': user_id, 'token': survey_token, 'seeking': 'STOP_SEEKING', 'redirect': 'http://www.tutut.org', }) response = self.app.post( '/api/employment-status/{}'.format(user_id), data='{"seeking": "STILL_SEEKING", "bobHasHelped": "YES_A_LOT"}', headers={'Authorization': 'Bearer ' + survey_token}, content_type='application/json') self.assertEqual(200, response.status_code) user = self.get_user_info(user_id, auth_token) self.assertEqual( ['STILL_SEEKING'], [s.get('seeking') for s in user.get('employmentStatus', [])])
def test_check_token(self) -> None: """Basic usage of check_token (round trip with create_token).""" login_token = auth.create_token('*****@*****.**', 'login') auth.check_token('*****@*****.**', login_token, 'login')
def test_check_token_wrong_role(self) -> None: """check_token fails if wrong role.""" login_token = auth.create_token('*****@*****.**', 'login') with self.assertRaises(ValueError): auth.check_token('*****@*****.**', login_token, 'unsubscribe')