Django check for error messages in context - django

I have a simple test:
def test_project_info_form_post_submission(self):
"""
Test if project info form can be submitted via post.
"""
# set up our POST data
post_data = {
'zipcode': '90210',
'module': self.module1.pk,
'model': self.model1.pk,
'orientation': 1,
'tilt': 1,
'rails_direction': 1,
}
...
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 302)
# test empty form
response = self.client.post(reverse(url))
self.assertEqual(response.status_code, 200)
#! test for general form error message
# now test invalid responses
post_data['zipcode'] = 'abcdefg'
response = self.client.post(reverse(url), post_data)
self.assertEqual(response.status_code, 200)
#! test for specific error message associated with zipcode
So the lines I am having trouble with are marked with shebangs. I know I should have messages in the context variable but can't seem to figure out the right ones to use.

You can test if the template contains your message in your TestCase with assertContains.

Related

Django API Client Get Request with Payload

I am looking to see if this is possible. If I send a get request with body in postman I get back results matching what I need.
Can we send a get request with a body using the APIClient?
Here is my code;
def setUp(self):
self.client = APIClient()
HistoryLog.objects.create(username='User1', log_time='2020-05-05', logon_time='08:00', logout_time='09:00')
HistoryLog.objects.create(username='User2', log_time='2020-05-05', logon_time='08:30', logout_time='10:00')
HistoryLog.objects.create(username='User3', log_time='2020-05-08', logon_time='08:40', logout_time='11:00')
HistoryLog.objects.create(username='User4', log_time='2020-05-10', logon_time='08:50', logout_time='12:00')
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
res = self.client.get(HISTORY_URL, payload)
self.assertEqual(res.status_code, status.HTTP_200_OK) -- this passes.
self.assertEqual(len(res.data), 2) -- this always come back that res.data is zero
A bit hacky, but seems to work:
def test_get_data(self):
payload = {'date': '2020-05-05', 'start_time': '06:00', 'end_time': '12:00'}
data, content_type = self.client._encode_data(payload, 'json')
res = self.client.generic('get', HISTORY_URL, data, content_type)
...

testing stripe on-boarding django with mock

i am having trouble trying to mock test the on-boarding process of stripe connect. I am just learning how to use mock and i am struggling with the StripeAuthorizeCallbackView. the process is as follows: A user reaches the StripeAuthorizeView which sends them to the stripe api to sign up for an account. Once they successfully sign up for an account their redirected back to my platform and stripe sends a temporary code which i then send back to stripe with my api keys. Once i have sent the information back to stripe they then return me credentials for the user being the stripe_user_id.
Here is the two views in question:
import urllib
import requests
class StripeAuthorizeView(LoginRequiredMixin, View):
def get(self, request):
url = 'https://connect.stripe.com/express/oauth/authorize?'
user = self.request.user
if user.account_type == 'Business':
business_type = 'company'
else:
business_type = 'individual'
params = {
'response_type': 'code',
'scope': 'read_write',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'redirect_uri': f'http://127.0.0.1:8000/accounts/stripe/oauth/callback',
'stripe_user[email]' : user.email,
'stripe_user[business_type]' : business_type,
'stripe_user[url]' : 'http://127.0.0.1:8000/accounts/user/%s/' %user.pk,
}
url = f'{url}?{urllib.parse.urlencode(params)}'
return redirect(url)
lass StripeAuthorizeCallbackView(LoginRequiredMixin, View):
def get(self, request):
code = request.GET.get('code')
if code:
data = {
'client_secret': settings.STRIPE_SECRET_KEY,
'grant_type': 'authorization_code',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'code': code
}
url = 'https://connect.stripe.com/oauth/token'
resp = requests.post(url, params=data)
stripe_user_id = resp.json()['stripe_user_id']
stripe_access_token = resp.json()['access_token']
stripe_refresh_token = resp.json()['refresh_token']
user = self.request.user
user.stripe_access_token = stripe_access_token
user.stripe_user_id = stripe_user_id
user.stripe_refresh_token = stripe_refresh_token
user.save()
notify.send(sender=user, recipient=user,
verb='You have succesfully linked a stripe account. You can now take payments for sales.',
level='info')
redirect_url = reverse('account', kwargs={'pk': user.pk})
response = redirect(redirect_url)
return response
else:
user = self.request.user
notify.send(sender=user, recipient=user,
verb='Your attempt to link a stripe account failed. Please contact customer support.',
level='warning')
url = reverse('account', kwargs={'pk': user.pk})
response = redirect(url)
return response
I am not very worried about testing the StripeAuthorizeView a lot. I am more trying to figure out how to test the StripeAuthorizeCallbackView. All i can figure out is that i will need to mock both the code returned and then mock the following requests.post. This test is important to confirm my platform is linking the users credentials after the on-boarding process. Any help on this will be greatly appricated.
edit:
So far i have the following :
#classmethod
def setUpTestData(cls):
cls.test_user = User.objects.create_user(
password='test',
full_name='test name',
email='test#test.com',
address='1 test st',
suburb='test',
state='NSW',
post_code='2000',
contact_number='0433335333' )
#patch('requests.get')
def test_authorizecallback_creates_stripe_details(self, get_mock):
code = requests.get('code')
user = self.test_user
self.client.login(email='test#test.com', password='test')
mocked = ({'stripe_user_id' : '4444','stripe_access_token' : '2222',
'stripe_refresh_token' : '1111' })
with mock.patch('requests.post', mock.Mock(return_value=mocked)):
response = self.client.get('/accounts/stripe/oauth/callback/',
{'code' : '1234'})
self.assertEqual(user.stripe_access_token, '222')
message = list(response.context.get('messages'))[0]
however i keep getting:
File "C:\Users\typef\Desktop\Projects\python_env\fox-listed\Fox-Listed\fox-listed\user\views.py", line 142, in get
stripe_user_id = resp.json()['stripe_user_id']
AttributeError: 'dict' object has no attribute 'json'
the actual response that the StripeAuthorizeCallBackView gives is:
{'access_token': 'sk_test_1KyTG74Ouw65KYTR1O03WjNA00viNjcIfO', 'livemode': False, 'refresh_token': 'rt_H3Vrhd0XbSH7zbmqfDyMNwolgt1Gd7r4ESBDBr5a4VkCzTRT', 'token_type': 'bearer', 'stripe_publishable_key': 'pk_test_**********', 'stripe_user_id': 'acct_1GVOpAF7ag87i2I6', 'scope': 'express'}
Looks like i got it, if there is a flaw here let me know but here is what i have:
class TestStripeAuthorizeCallbackView:
#patch('user.views.requests')
def test_authorizecallback_creates_stripe_details(self, requests_mock):
json = { 'stripe_user_id' : '4444', 'access_token' : '2222', 'refresh_token' : '1111'}
requests_mock.post.return_value.json.return_value = json
user = mixer.blend('user.CustomUser', stripe_user_id=None, access_token=None, refresh_token=None)
req = RequestFactory().get('/', data={'code' : '1234'})
middleware = SessionMiddleware()
middleware.process_request(req)
req.session.save()
messages = FallbackStorage(req)
setattr(req, '_messages', messages)
req.user = user
resp = StripeAuthorizeCallbackView.as_view()(req)
assert resp.status_code == 302 ,'should redirect to success url'
assert user.stripe_user_id == '4444', 'should assign stripe_user_id to user'
assert user.stripe_access_token == '2222', 'should assign an access_token'
assert user.stripe_refresh_token == '1111', 'should assign a refresh_token'
What you're describing isn't mocking so much as it is end-to-end testing, connecting actual test accounts, which you can do.
As long as you're using a test client_id then when you are redirected to Stripe to create the account you can skip the form via a link and get directed back to your site with a real (test mode) oauth code.
Essentially you can set this up and actually go through the flow to create & connect new disposable test Stripe accounts.

django lazy translation sometimes appears in response.data in unit tests

I'm writing unit tests for my django api written with django-rest-framework, and I'm encountering seemingly inconsistent response data from calls that generate 400_BAD_REQUEST.
When I make a request that fails because it references an invalid primary key, repr(response.data) is a string that I can check for the substring "Invalid pk". But when I make a request that fails because it's trying to create a non-unique object, repr(response.data) contains {'name': [<django.utils.functional.__proxy__ object at 0x7f3ccdcb26d0>]} instead of the expected {'name': ['This field must be unique.']}. When I make an actual POST call to the real server, I get the expected 400 response with {'name': ['This field must be unique.']}.
Here's a code sample:
class GroupViewTests(APITestCase):
def test_post_existing_group(self):
"""
Use POST to update a group at /groups
"""
# create a group
# self.group_data returns a valid group dict
data = self.group_data(1)
url = reverse('group-list')
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# create the same group again
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# this fails
self.assertIn('must be unique', repr(response.data))
def test_create_group_with_nonexistent_user(self):
"""
Create a group with an invalid userid in it.
"""
url = reverse('group-list')
data = self.group_data(5)
data.update({'users': ['testnonexistentuserid']})
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# this succeeds
self.assertIn('Invalid pk', repr(response.data))
I'm using only the default middleware.
From what I've found online, the __proxy__ object is a representation of a lazy translation, which can be rendered as a string by calling unicode(obj) or obj.__unicode__. And indeed, response.data['name'][0].__unicode__ does return the expected value, but calling unicode(response.data) instead of repr(response.data) still returns the object identified by memory address.
Can anyone explain what's going on here, and how to fix it? I can work around the issue for now, but I'd like to know the "real way" to fix the problem. Is it possibly a bug in django or django-rest-framework?
Use response.content instead of response.data.
response.content contains the json output just like your client will receive it. It's also useful if you have custom renderer that change the response because response.data is not rendered, but response.content yes.
E.g :
import json
# we have to parse the output since it no more a dict
data = json.loads(response.content)
# if `response.content` is a byte string, decode it
data = json.loads(response.content.decode('utf-8'))

Django 1.6.1 : Transaction error. You can't execute queries until the end of the atomic block in unitest

Django 1.6.1 on Windows with Postgres
I m using unittest to test a view that upload a .cfg file to server. I am facing 2 error called
(1) TransactionManagementError: An error occurred in the current transaction. You can't execute queries until the end of the atomic block.
(2) OSError: [Errno 22] Invalid argument
I am writing down my view and test below.
Views.py
def upload(request, device_id):
owner=account.objects.get(user=request.user)
dev = get_object_or_404(device, pk=device_id, owner=owner)
state = 0
# Handle file upload
if request.method == 'POST':
form = UploadConfigForm(request.POST, request.FILES)
if form.is_valid():
new_file = form.save(commit=False)
#now ask the device to download the config file
sess, action, action_opt = add_device_action(device=dev, action=Action.AC_CONFIG_FILE_SET)
#get the action related to this file
if action_opt:
new_file.act = action_opt
else:
new_file.act = action
new_file.save()
form.save_m2m()
#add the payload to the action
if action_opt:
action_opt.payload = new_file.payload()
action_opt.save ()
else:
action.payload = new_file.payload()
action.save ()
if settings.RENDER_JSON_INSTEAD_OF_HTML:
# Render as an json response
resp = action.descriptor()
return HttpResponse(json.dumps(resp), content_type="application/json")
else:
# Redirect to the document list after POST
# return HttpResponseRedirect('..')
state = 1
else:
form = UploadConfigForm() # A empty, unbound form
tests.py
class DevAPISimulDevTests(TestCase):
"""
Class that test the device API
"""
fixtures = ['test_auth.json', 'test_users.json', 'test_device.json']
def setUp(self):
"""
"""
ret = self.client.login(username='cws', password='cws123')
self.assertEqual(ret, True)
def test_api_sim_usr_upload_config_file(self):
"""
simulate the upload of the config file ba the user. submit with a file that is ok
"""
logger.debug('test_upload_config_file')
dev_id = 1
dev = device.objects.get(pk=dev_id)
url = reverse('device-upload', kwargs={'device_id':dev_id})
myfile = open('device/fixtures/cfg_ok.csr','r')
# with transaction.atomic():
response = self.client.post(url, {'config_file':myfile})
logger.debug('POST[%s]:[%s]', url, response.status_code)
self.assertEqual(response.status_code, 200)
ad = json.loads(response.content)
# Check the ad we think we should receive.
self.assertEqual(ad[Action.AD_EP_URL_BASE], settings.API_BASE_URL)
self.assertEqual(ad[Action.AD_EP_ACTION], settings.API_EP_ACK)
self.assertEqual(ad[Action.AD_EP_ERROR], settings.API_EP_ERR)
self.assertEqual(ad[Action.AD_DEVICE_CODE], dev.access_code)
self.assertEqual(ad[Action.AD_ACTION_CODE], Action.AC_ACKNOWLEDGE)
self.assertIsNone(ad.get(Action.AD_ACTION_PAYLOAD))
self.assertIsNone(ad.get(Action.AD_ACTION_WAIT))
# Check the DB
# We should have 1 session
self.assertEqual(dev.session_set.count(), 1)
sess = Session.objects.first()
self.assertEqual(sess.state, Session.STATE_SMS_SENT)
# We should have 2 actions more
self.assertEqual(sess.action_set.count(), 2)
# The first action should have the same token as in the SMS and the status should be in progress
first_act = sess.action_set.first()
self.assertEqual(first_act.next_token, ad[Action.AD_EP_TOKEN])
self.assertEqual(first_act.status, Action.STATUS_IN_PROGRESS)
# The second action should have the same token as in the SMS and the status should be queued
next_act = sess.action_set.last()
self.assertEqual(next_act.current_token, ad[Action.AD_EP_TOKEN])
self.assertEqual(next_act.status, Action.STATUS_QUEUED)
# test the status
status_should_be = {u'0': {u'state': u'in progress', u'state_label': u'processing', u'label': u'contacting device'},
u'1': {u'state': u'queued', u'state_label': u'not started', u'label': u'uploading config file to device'}}
self.test_sessionstatus(status_should_be)
return ad
# except IntegrityError:
# pass
def test_api_sim_dev_init_session (self):
"""
API : simulate the device receiving the first AD trhough SMS and initiating the session
"""
# try:
logger.debug('test_api_sim_dev_init_session')
# with transaction.atomic():
ad = self.test_api_sim_usr_upload_config_file()
# validate that the url is correct
url = reverse('device-api_ack', kwargs={'token':ad [Action.AD_EP_TOKEN]})
url2 = ''.join (['/device/', ad [Action.AD_EP_TOKEN], '/', ad[Action.AD_EP_ACTION]])
self.assertEqual(url, url2)
# Simulate the device processing the first ad of the session
# retrieve the next command from the server
response = self.client.get(url)
logger.debug('GET[%s]:[%s]', url, response.status_code)
# check the answer
self.assertEqual(response.status_code, 200)
ad2 = json.loads(response.content)
self.assertTrue(type(ad2) is dict)
# Check the ad we think we should receive.
self.assertEqual(ad2[Action.AD_ACTION_CODE], Action.AC_CONFIG_FILE_SET)
self.assertEqual(ad2[Action.AD_EP_ACTION], settings.API_EP_CFG)
self.assertTrue(type(ad2[Action.AD_ACTION_PAYLOAD]) is dict)
#TODO: will need on time to understand why this asssrt fail.
# self.assertContains(ad2[Action.AD_ACTION_PAYLOAD]['url'], '.csr')
self.assertIsNone(ad2.get(Action.AD_DEVICE_CODE))
self.assertIsNone(ad2.get(Action.AD_EP_URL_BASE))
self.assertIsNone(ad2.get(Action.AD_EP_ERROR))
# Check the DB
# We should have 1 session in STATE_WAIT_DEV_REQ
dev_id = 1
dev = device.objects.get(pk=dev_id)
self.assertEqual(dev.session_set.count(), 1)
sess = Session.objects.first()
self.assertEqual(sess.state, Session.STATE_WAIT_DEV_REQ)
# We should still have 2 actions
self.assertEqual(sess.action_set.count(), 2)
# The first action should have a status done
first_act = sess.action_set.first()
self.assertEqual(first_act.status, Action.STATUS_DONE)
self.assertEqual(first_act.next_token, ad[Action.AD_EP_TOKEN])
# The second action should have the same token as in the SMS and the status should be queued
next_act = sess.action_set.last()
self.assertEqual(next_act.current_token, ad[Action.AD_EP_TOKEN])
self.assertEqual(next_act.next_token, ad2[Action.AD_EP_TOKEN])
self.assertEqual(next_act.status, Action.STATUS_IN_PROGRESS)
# test the status
status_should_be = {u'0': {u'state': u'done', u'state_label': u'done', u'label': u'contacting device'},
u'1': {u'state': u'in progress', u'state_label': u'processing', u'label': u'uploading config file to device'}}
self.test_sessionstatus(status_should_be)
return ad2
Please suggest what I am doing wrong here, If code being pasted here does not concern errors, please suggest what code causes this error , as I cant figure out the origin of this errors..
might want to add:
'ATOMIC_REQUESTS': True,
under
DATABASES = {
'default': {
'ENGINE':whatever,
'ATOMIC_REQUESTS': True,
}
}

Testing Django 1-5 Reset Password Form - how to generate the token for the test?

With the following test, the token is not recognised as valid. In my manual test, it's working so I'm missing something in the way the password is generated I guess.
def test_actual_reset_password(self):
new_password = "myNewPassword012*"
token_generator = PasswordResetTokenGenerator()
user = UserFactory.create()
token = token_generator.make_token(user=user)
response = self.assert_page_loading(path="/forgot-password/reset/{0}/".format(token))
print response
# That loads the page with the error message mentioning that the token was already used
# So I cannot carry on:
form = response.form
form['new_password1'] = new_password
form['new_password2'] = new_password
response = form.submit()
In the django source code, in the PasswordResetForm, I've found this code; I can't see what the difference is:
def save(self, ..., token_generator=default_token_generator, ...):
"""
Generates a one-use only link for resetting password and sends to the
user.
"""
...
for user in self.users_cache:
...
c = {
...
'token': token_generator.make_token(user),
...
}
...
send_mail(subject, email, from_email, [user.email])
Ok, I was just searching for info on how to do this and your question prompted me to figure it out myself. I'm not sure if you're still working on this, but here's how I got it to work:
from django.core import mail
# First we get the initial password reset form.
# This is not strictly necessary, but I included it for completeness
response = self.c.get(reverse('password_reset'))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template_name, 'authentication/password_reset_form.html')
# Then we post the response with our "email address"
response = self.c.post(reverse('password_reset'),{'email':'fred#home.com'})
self.assertEqual(response.status_code, 302)
# At this point the system will "send" us an email. We can "check" it thusly:
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, 'Password reset on example.com')
# Now, here's the kicker: we get the token and userid from the response
token = response.context[0]['token']
uid = response.context[0]['uid']
# Now we can use the token to get the password change form
response = self.c.get(reverse('password_reset_confirm', kwargs={'token':token,'uidb64':uid}))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template_name, 'authentication/password_reset_confirm.html')
# Now we post to the same url with our new password:
response = self.c.post(reverse('password_reset_confirm',
kwargs={'token':token,'uidb36':uid}), {'new_password1':'pass','new_password2':'pass'})
self.assertEqual(response.status_code, 302)
And that's it! Not so hard after all.
This is how I did it for a functional test:
def test_password_reset_from_key(self):
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import base36_to_int, int_to_base36
user = User.objects.all()[:1].get()
token = default_token_generator.make_token(user)
self.get("/accounts/password/reset/key/%s-%s/" % (int_to_base36(user.id), token))
self.selenium.find_element_by_name("password1").send_keys("password")
self.selenium.find_element_by_name("password2").send_keys("password")
self.selenium.find_element_by_name("action").submit()
alert = self.selenium.find_element_by_css_selector(".alert-success")
self.assertIn('Password successfully changed.', alert.text)