def test_author_list_equality_with_invalid_authentication(self) -> None: """ Similar to the previous test except the request sent to /api/authors/ is invalid with improper permissions - once with an Author with no permissions and once with no authentication headers at all. """ # Let's check for a request with no authorization response: Response = self.client.get(self.url) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual( data, {'detail': 'Authentication credentials were not provided.'}) # Now lets check with an Author without permissions. # Select the underprivileged author randomly. author: Author = random.choice(self.authors) self.client.credentials( HTTP_AUTHORIZATION=u.auth_header(author.get_key())) response: Response = self.client.get(self.url) data: typing.Dict[typing.Any, typing.Any] = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual( data, {'detail': 'You do not have permission to perform this action.'})
def test_invalid_update_request_with_taken_username(self): """ Tests to update an Author instance with a username that has already been taken - in this case, the username of the second author constructed only for this test case inside of the setUpTestData because it will be deleted once the test case finishes running automatically by Django. This should raise a 409 error with same error message as AuthorCreateAPIView. tl;dr - this makes an authenticated request to update mentix02's Author instance but with a username that already belongs to another user (the cls.temporary_author) - that returns an error and this test cases verifies. """ self.client.credentials( HTTP_AUTHORIZATION=u.auth_header(self.author.get_key())) response: Response = self.client.patch( BASE_URL + '/update/', data={'username': self.temporary_author.username}) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_409_CONFLICT, msg=data) self.assertEqual(data, { 'detail': f"User '{self.temporary_author.username}' already exists." })
def test_author_registration_with_existing_username(self) -> None: """ Makes a request to /api/authors/create/ to create a user but expects failure because we'll provide a username that already exists in the database by picking an Author randomly from db. """ # Create a fake test Author author: Author = create_author() # Construct POST request with taken username response: Response = self.client.post( BASE_URL + '/create/', { 'password': '******', 'bio': fake.text(120), 'email': fake.email(), 'username': author.username, 'first_name': fake.first_name(), }) data: typing.Dict[typing.Any, typing.Any] = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_409_CONFLICT) self.assertEqual( data, {'detail': f"User '{author.username}' already exists."})
def test_invalid_authentication(self): """ Similar to test_valid_authenticate except this tests for invalid credentials and incomplete data. Easy. """ # Test for incomplete data. for field in ('username', 'password'): response: Response = self.client.post( BASE_URL + '/authenticate/', data={ field: random.choice(self.authors). username # Random value because it doesn't really matter }) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertEqual( data, { 'detail': f"Field '{'password' if field.startswith('u') else 'username'}' not provided." }) # Test for invalid password for author in self.authors + [self.super_author]: response = self.client.post( BASE_URL + '/authenticate/', data={ 'username': author.username, 'password': '******' # invalid password }) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) self.assertEqual(data, {'detail': 'Invalid credentials.'})
def test_verification_with_invalid_token(self) -> None: """ Makes a valid request to an invalid URL containing the secret_key that does not belong to any existing Author. This should result in nothing but an error response with status code 404. """ uuids: typing.List[str] = [] for i in range(2, 5): uuids.append(str(uuid.uuid5(uuid.uuid1(1), f'abcd123456{i}'))) for temp_uuid in uuids: response: Response = self.client.get( f'/api/authors/verify/{temp_uuid}/') data = u.get_json(response) self.assertEqual(response.status_code, 404) self.assertEqual(data, {'detail': 'Not found.'})
def test_author_list_equality_with_valid_authentication(self) -> None: """ Makes a valid (authenticated) request to /api/authors/ to compare list results with AuthorListSerializer serialized queryset data. Should return with a status code 200. Request is authenticated with authtoken for the super_author account inside of the Header. """ # Set the Authorization header to the appropriate # format as the rest_framework expects using utils. self.client.credentials( HTTP_AUTHORIZATION=u.auth_header(self.super_author.get_key())) response = self.client.get(self.url) data = u.get_json(response) self.assertEqual(data, self.serialized_data, msg=data) self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_valid_authentication(self): """ Makes a valid POST request to /api/authors/authenticate/ by provided a valid username & password combo and compares authtoken provided to the one in the database. """ for author in self.authors + [self.super_author]: response: Response = self.client.post( BASE_URL + '/authenticate/', data={ 'username': author.username, 'password': '******' # Might be a better way to store random passwords. }) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(data, {'token': author.get_key()})
def make_test(self, model_type: str, model_serializer, url_name: str): """ A custom generic test maker to not duplicate the same code over and over - it takes a model_type that is a string of model types. It could be "articles" or "topics" so that a getattr call can get the data members from some string formatting as the variables are named following a certain syntax. Then it makes a GET request to the url_name that was provided with the apt parameters (in this case, the username). Then it's simply a matter of asserting cases. """ for author_id in [1, 2]: username = getattr(self, f'author_{author_id}').username instances = getattr(self, f'{model_type}_by_author_{author_id}') instances_data = model_serializer(instances, many=True).data response = self.client.get( reverse(url_name, kwargs={'username': username})) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(data['count'], len(instances_data))
def test_author_registration_with_invalid_data(self) -> None: """ Makes invalid requests to /api/authors/create/ by sending incomplete or poorly formatted data - such as having a space in a username or not including a required param in the POST body. Takes care of two error responses in one test case. """ # First check without any parameters. It should return with an # error message for the username field since that's what the view # tried to check for first - before anything else. response: Response = self.client.post(BASE_URL + '/create/') data: typing.Dict[typing.Any, typing.Any] = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertEqual(data, {'detail': "Field 'username' not provided."}) # Now check with an invalid username - one having a space and a # dollar sign. We dont' even need to send any more data (other than email) # since the view immediately returns an invalid username (or email) error # if they (email and username) aren't valid. response = self.client.post(BASE_URL + '/create/', { 'username': '******', 'email': '*****@*****.**' }) data = u.get_json(response) self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertEqual( data, { 'detail': 'Requires 150 characters or fewer. Letters, digits and @/./+/-/_ only.' }) post_data = { 'bio': fake.text(120), 'email': fake.email(), 'password': '******', 'username': fake.user_name(), 'first_name': fake.first_name(), } # Now check with two missing fields - password and bio. for missing_field in ['bio', 'password']: # Make a local for loop specific copy # to delete required missing field. d = post_data.copy() del d[missing_field] response = self.client.post(BASE_URL + '/create/', d) data = u.get_json(response) # It should raise a 'bio' field error since that's entered into the data # dictionary that's passed as params to the create() Author method first. self.assertEqual(response.status_code, status.HTTP_422_UNPROCESSABLE_ENTITY) self.assertEqual( data, {'detail': f"Field '{missing_field}' not provided."})