How to mock internal function's side effect - python-3.x

HI I have a simple function. Which internally calls db but during local testing it can't connect DB so for that I have wrote specific exception and I want do side effect for same but it's not working.
Here is my code
def support_issue_contact(message, message_headers):
# Check if reporter_email_address is present in message
logger = message_headers.get('logger')
reporter_email = message.get('reporter_email_address', None)
if reporter_email is None:
return None
elif validators.email(reporter_email):
# look up message in our contact table to see if it exists
rule = {
"lookup_documents": [{
"document_type": "hub",
"hub_table": "hubs",
"entity": "contact",
"lookup_key_path": "reporter_email_address"
}]
}
try:
retrieve_documents(message, rule)
except StaleEventException:
return None
# Catch the retrieve_documents function if no 'Item' exists in the response it gets from Dynamo
# If no 'Item' exists then no contact exists so it should create a new contact
except KeyError as valid_exception:
if 'Item' in valid_exception.args:
# validate context to see if it is on 'Item'
contact_message = {
"business_key": reporter_email,
"source_system": 'gsac',
"source_system_id": reporter_email,
"source_sequence": message.get('source_sequence'),
"email": reporter_email,
"full_name": message.get('reporter_display_name', ''),
"customer_domain":
derive_customer_domain(reporter_email),
"status": "ACTIVE",
"id": reporter_email
}
return {
"payload": contact_message,
"schema": "it-bdm/contact-schema-v3.5.json",
"type": "avi:hydra-gsac:contact:upsert",
}
elif ('payload' in valid_exception.args) or ('satellite_name' in valid_exception.args):
# This means that the item exists within the hubs so we don't want to do anything
return None
else:
raise valid_exception
# All other exceptions should be raised
except Exception as e:
logger.error(e.__str__())
raise e
else:
return None
And I want that retrieve_documents function should raise CustomKeyError so I wrote these way and both are not working.
class SupportIssueContactTest(unittest.TestCase):
raw_event = parse_json_file(os.path.join(DIR_TEST_DATA, 'support-issue', 'support_issue.json'))
transformed_event = parse_json_file(os.path.join(DIR_TEST_DATA, 'support-issue', 'transformed_support_issue.json'))
def test_support_issue_contact_context(self):
with mock.patch('src.datavault_helper.retrieve_documents') as retrieve_documents_mock:
retrieve_documents_mock.side_effect = CustomKeyError()
assert self.transformed_event == support_issue_contact(message=self.raw_event, message_headers={'logger': config.logger})
#mock.patch('src.datavault_helper.retrieve_documents')
def test_support_issue_contact_decorator(self, retrieve_documents_mock):
retrieve_documents_mock.side_effect = CustomKeyError()
assert self.transformed_event == support_issue_contact(message=self.raw_event,
message_headers={'logger': config.logger})

I figured out the answer.
def test_support_issue_contact_context(self):
with mock.patch('path_of_function_where_it_is_called_for_mock') as retrieve_documents_mock:
retrieve_documents_mock.side_effect = CustomKeyError()
assert self.transformed_event == support_issue_contact(message=self.raw_event, message_headers={'logger': config.logger})

Related

Python Code runs in VS Code but not discord host site

I have written a discord bot in python in VS code. The code consists of 1 init.py file, and 9 Cog files.
example of my init.py:
#bot.event
async def on_ready():
guild_count = 0
for guild in bot.guilds:
if guild.id == 941021482144903178:
guild_count += 1
for jsonguild in guilds["guilds"]:
if jsonguild["id"] == str(guild.id):
break
else:
guilds["guilds"].append({
"id": str(guild.id),
"prefix": "!",
"tornapikey": "",
"tornapikey2": "",
"tornapikey3": ""
})
dbutils.write("guilds", guilds)
if str(guild.id) not in aractions:
aractions[guild.id] = {
"lastscan": 0,
"requests": []
}
dbutils.write("armory", aractions)
for member in guild.members:
if str(member.id) in users:
continue
if member.bot:
continue
users[member.id] = {
"tornid": "",
"tornapikey": "",
"generaluse": False
}
dbutils.write("users", users)
print(f'Bot is in {guild_count} guilds.')
print(f'{bot.user} has connected to Discord!')
await bot.add_cog(admin.Admin(botlogger, bot, client))
example of admin.py:
class Admin(commands.Cog):
def __init__(self, logger, bot, client):
self.logger = logger
self.bot = bot
self.client = client
#commands.command()
#commands.cooldown(1, 5, commands.BucketType.user)
async def config(self, ctx, arg=None, value=None):
'''
Returns the current configuration of the bot
'''
if not check_admin(ctx.message.author) and dbutils.get_superuser() != ctx.message.author.id:
embed = discord.Embed()
embed.title = "Permission Denied"
embed.description = f'This command requires {ctx.message.author.name} to be an Administrator. ' \
f'This interaction has been logged.'
await ctx.send(embed=embed)
self.logger.warning(f'{ctx.message.author.name} has attempted to run config, but is not an Administrator')
return None
embed = discord.Embed()
if not value:
embed.title = "Value Error"
embed.description = "A value must be passed"
elif arg == "key":
data = dbutils.read("guilds")
for guild in data["guilds"]:
if guild["id"] == str(ctx.guild.id):
guild["tornapikey"] = str(value)
dbutils.write("guilds", data)
self.logger.info(f'{ctx.message.author.name} has set the primary Torn API Key.')
embed.title = "Torn API Key"
embed.description = f'The Torn API key for the primary faction has been set by {ctx.message.author.name}.'
await ctx.message.delete()
elif arg == "key2":
data = dbutils.read("guilds")
for guild in data["guilds"]:
if guild["id"] == str(ctx.guild.id):
guild["tornapikey2"] = str(value)
dbutils.write("guilds", data)
self.logger.info(f'{ctx.message.author.name} has set the secondary Torn API Key.')
embed.title = "Torn API Key"
embed.description = f'The Torn API key for the secondary faction has been set by {ctx.message.author.name}.'
await ctx.message.delete()
elif arg == "key3":
data = dbutils.read("guilds")
for guild in data["guilds"]:
if guild["id"] == str(ctx.guild.id):
guild["tornapikey3"] = str(value)
dbutils.write("guilds", data)
self.logger.info(f'{ctx.message.author.name} has set the secondary Torn API Key.')
embed.title = "Torn API Key"
embed.description = f'The Torn API key for the secondary faction has been set by {ctx.message.author.name}.'
await ctx.message.delete()
else:
embed.title = "Configuration"
embed.description = "This key is not a valid configuration key."
if embed is not None:
await ctx.send(embed=embed)
These codes run perfectly in VS code, connects to Discord and all of the commands work as they should. When I try and run them on my Discord Bot Hosting Server (Something.Host) I get the following error with no code changes:
Ignoring exception in on_ready
Traceback (most recent call last):
File "/home/container/.local/lib/python3.8/site-packages/discord/client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "__init__.py", line 85, in on_ready
TypeError: object NoneType can't be used in 'await' expression
await bot.add_cog(admin.Admin(botlogger, bot, client))
I have uninstalled and reinstalled numerous packages. My python verion on VS is 3.10.7, and on the server it is python 3.10x.
I have also tried adding this in admin.py
async def setup(logger, bot, client):
await bot.add_cog(Admin(logger, bot, client))
and this in init.py
files = ['admin']
for cogs in files:
bot.load_extension(cogs)
but this didn't work either.
Please could you help me find out why my Cog is returning a NoneType in my server, but not in VS code?
My bot logged a whole bunch of DEBUG stuff when trying to run, will try and provide a link to the file

Want to return current page limit in response in Django Rest Framework

Want to return current page limit current_items_per_page in response.
pagination.py
class CustomPageNumberPagination(pagination.PageNumberPagination):
page_size = 10 # Number of objects to return in one page
page_size_query_param = 'total_count'
max_page_size = 20
page_query_param = 'page_no'
page_size_query_param = 'limit_pr_page'
def generate_response(self, query_set, serializer_obj, request):
Try:
page_data = self.paginate_queryset(query_set, request)
except NotFoundError as e:
return Response({"response": False, "return_code": "NotFoundError", "result": {}, "message": errors["invalid_page"]}, status=status.HTTP_400_BAD_REQUEST)
serialized_page = serializer_obj(page_data, many=True)
return Response({
"response": True,
"return_code": "success",
"result":serialized_page.data,
"message": "Success",
'page_no': self.page.number,
'current_items_per_page': self."?",
'max_limit_per_page': self.max_page_size,
'last_page': self.page.paginator.num_pages,
'total_items': self.page.paginator.count,
},
status=status.HTTP_200_OK
)
You can use len() of current page's object_list:
'current_items_per_page': len(self.page.object_list),

Overwrite django rest default validation errors handler

I am using django-rest for my back-end and want to overwrite default errors for fields.
My current code looks like this.
class DeckSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
"id",
"title",
"get_absolute_url",
"description",
"price",
"image",
"category_id",
"category",
"title"
)
extra_kwargs = {
'title': {"error_messages": {"required": "Title cannot be empty"}},
'image': {"error_messages": {"required": "Image cannot be empty"},}
}
After writing these 2 kwargs i realised i would just be repeating something that could be solved by code.
By default the serializer validation returns this when the field is missing {title:"This field is required"}.
Is there any way that i can overwrite the current message so it can display directly the name_of_the_field + my_message . Example {title: Title is required}
I am not looking on how to write custom error message for a single field , im looking on how to write generic costum messages for every field that for example is missing or null.
We can achieve it by writing a custom exception handler.
Here is how a custom response might look like:
{
"status_code": 400,
"type": "ValidationError",
"message": "Bad request syntax or unsupported method",
"errors": [
"username: This field may not be null.",
"email: This field may not be null.",
"ticket number: This field may not be null."
]
}
We have to create a file: exception_handler.py in our project directory with the code that follows; I use utils for this kind of purposes. You can also put this code anywhere you like, but I prefer to have it in a separated file dedicated for this purpose.
from http import HTTPStatus
from rest_framework import exceptions
from rest_framework.views import Response, exception_handler
def api_exception_handler(exception: Exception, context: dict) -> Response:
"""Custom API exception handler."""
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exception, context)
# Only alter the response when it's a validation error
if not isinstance(exception, exceptions.ValidationError):
return response
# It's a validation error, there should be a Serializer
view = context.get("view", None)
serializer = view.get_serializer_class()()
errors_list = []
for key, details in response.data.items():
if key in serializer.fields:
label = serializer.fields[key].label
help_text = serializer.fields[key].help_text
for message in details:
errors_list.append("{}: {}".format(label, message))
elif key == "non_field_errors":
for message in details:
errors_list.append(message)
else:
for message in details:
errors_list.append("{}: {}".format(key, message))
# Using the description's of the HTTPStatus class as error message.
http_code_to_message = {v.value: v.description for v in HTTPStatus}
error_payload = {
"status_code": 0,
"type": "ValidationError",
"message": "",
"errors": [],
}
# error = error_payload["error"]
status_code = response.status_code
error_payload["status_code"] = status_code
error_payload["message"] = http_code_to_message[status_code]
error_payload["errors"] = errors_list
# Overwrite default exception_handler response data
response.data = error_payload
return response
The main idea comes from here, but I changed it to my needs. change it as you see fit.
Don't forget to set it as your default exception handler in you settings.py file:
REST_FRAMEWORK["EXCEPTION_HANDLER"] = "utils.exception_handler.api_exception_handler";

TypeError: get_object() takes 1 positional argument but 2 were given

I am trying to create the object only if there is no user is present in the table or else update the field. My implementation is :
class UserMobileViewSet(generics.ListCreateAPIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = UserMobileDevice.objects.all()
serializer_class = UserMobileSerializer
def get_user(self, id):
try:
UserMobileDevice.objects.get(user=id)
except User.DoesNotExist:
return None
def create(self, request, *args, **kwargs):
myModel = None
id = request.data.get("user") #id = 52 pk = 2
print(id)
if id:
myModel=self.get_user(id)
print(myModel)
if myModel:
return self.update(request, *args, **kwargs)
else:
return self.create(request, *args, **kwargs)
My table schema is as :
[
{
"id": 1,
"token": "f5a906ec934425d00fc40e3b260eca2c48965d0a",
"device_model": "Samsung J6",
"os": "Android 9",
"os_version": "9",
"created_at": "2019-09-12T03:36:11.630303Z",
"updated_at": "2019-09-12T03:36:13.724340Z",
"user": 2
},
....
]
I am getting error as :
TypeError: get_object() takes 1 positional argument but 2 were given
I am very new to DRF, Not sure how i will get the object from the id. Any suggestion will be of great help!!
Edit: Got the view/viewset stuff mixed up.
You can define your own to do this.
get_object() takes no parameters, and gets the value indicated in the URL when you use a router to create your routes. A typical url would look like /users/30/, and get_object() would return User(id=30).
def get_user(self, id):
# returns null if no match
return User.objects.filter(pk=id).first()
def get_user(self, id):
# catch exception and manually return none
try:
User.objects.get(pk=id)
except User.DoesNotExist:
return None

How do i test except Errors using side_effect in python unittest mock?

I am a junior developer and trying to write some unittests for our API endpoints. Below is the class that i am testing and the actual test that runs without any issue. (But i am still hesitant that it's hitting my methods). My question is how can i improve my test and also make sure it covers exception(in this case ValueError, SystemError, Exception) by using side_effects(or any better suggestions) from python mock? I read python mock documentation but still can't figure out how to improve and importantly test exceptions.
we use flask microframework, python3.x,
--- Class that i am testing:
#USER_MOD.route('', methods=[HttpMethods.HTTP_POST])
#jwt_required
def create_user():
"""
Private end point for creating users of the system
:return: json
"""
response = ""
try:
logger_obj.info("Request : POST : Create User: {} ".format(request.base_url))
# validating input request
if ValidationUtils.validate_request(request, "CREATE_USER"):
logger_obj.debug("Request Validation Successful")
response = UserService().create_new_user(request)
logger_obj.debug("User Created: {} ".format(response))
return jsonify(response), HTTPStatus.OK
except ValueError as error:
logger_obj.error("ValueError create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.BAD_REQUEST
except SystemError as error:
logger_obj.error("SystemError create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.INTERNAL_SERVER_ERROR
except Exception as error:
logger_obj.error("Exception create_user() : {}".format(error))
response = ApplicationUtils.create_api_response(status=ResponseConstants.MSG_ERROR, message=str(error))
return jsonify(response), HTTPStatus.UNAUTHORIZED
finally:
logger_obj.info("Response : POST : Create User: {} : {}".format(request.base_url, response))
--- Test for above class:
class UserApiTests(DataTestCase): //(or i can use unittest.TestCase)
def setUp(self):
os.environ.update(self.config.to_env())
self.flask_app = make_flask_app()
self.client = self.flask_app.test_client()
self.flask_app.testing = True
#patch('usermgmt.app.services.user_service.UserService.create_new_user')
def test_create_user(self, mock_create_new_user):
# Given
mock_create_new_user.return_value.status_code = 200
mock_create_new_user.return_value = {
"status": "SUCCESS"
}
data_to_post = {
"name": "Test2",
"email": "new-user2#entity.com",
"entity_id": 1,
"is_active": True,
"product_roles": [
{"product_id": 1, "role_id": 4},
{"product_id": 2, "role_id": 4}
],
}
# When
response = self.client.post('/api/usermgmt/users', data=json.dumps(data_to_post), headers={
"Authorization": "Bearer {}".format(get_jwt(identity=self), "Content-Type: application/json")
})
data = response.data
json_data = json.loads(data)
# Then
self.assertEqual(response.status_code, 200)
self.assertEqual(json_data['status'], "SUCCESS")
During my practice I've found that approach quite good.
class TestCreateUser(TestCase):
def test_works_in_correct_case(self):
# equal to your test_create_user
#patch("usermgmt.app.services.user_service.UserService.create_new_user")
def test_handles_value_error_in_create_new_user(self, mock_create_new_user):
mock_create_new_user.side_effect = ValueError
# Your preparation flow here
response = self.client.post('/api/usermgmt/users') # simplified call here is just an example
self.assertEqual(response.status_code, 400)
# check if response body is what you've expected, etc.

Resources