API end point testing using behave - python

I have a django rest API end point login which takes username and password in form of json object as below.
{
username: email,
password: password,
}
and returns a json object containing a token
{
token : 0234jh324234j2hiy342
}
Now i want to write a test in behave. I have following feature file.
Feature: Login User
By providing different credentials we check if our login API end point is working as expected or not
Scenario: Login User by Providing Authentication Credentials
Given I provide user authentication credentials
Then I must get a reponse with status code 200 and a jSon object with token
and following is my auth.py file
from behave import *
import requests
import json
#given('I have user authentication credentials')
def set_impl(context):
url = 'https://example.com/v1/login'
headers = {'content-type': 'application/json'}
body = {
"username": "xyz#email.com",
"password": "abcdef123",
}
#when('I make an http post call')
def step_impl(context):
context.res = requests.post(url, data=json.dumps(body), headers=headers)
#then('I must get a reponse with status code 200 and a jSon object with token')
def step_impl(context):
assert context.res.status == 200
I am unable to access the url, header and body from #given decorator in #when decorator. And how can i check the json in response against my expected json.

Per #KlausD.'s suggestion, you should add your variables to the behave's context object. I've edited your code to add your variables as the context object's attributes.
from behave import *
import requests
import json
#given('I have user authentication credentials')
def set_impl(context):
context.url = 'https://example.com/v1/login'
context.headers = {'content-type': 'application/json'}
context.body = {
"username": "xyz#email.com",
"password": "abcdef123",
}
#when('I make an http post call')
def step_impl(context):
context.res = requests.post(context.url, data=json.dumps(context.body), headers=context.headers)
#then('I must get a reponse with status code 200 and a jSon object with token')
def step_impl(context):
assert context.res.status == 200
As for checking the JSON in your response against your expected JSON...
Check out the requests package's response object here to find out how to get the response object's attributes.
Open your own expected JSON file via open(), grab the value that corresponds to the token key, and do an assert expectedToken == responseToken, or something of that sort.

Related

In odoo's controller file, how to change the json response format when the type is json?

The application/json in the request header and json string in the request body when I initiate an http request , the Odoo server receives the request, but the json returned to the client is not what I want to return.
Here are two additional key,jsonrpc,id,result.The dictionary corresponding to the key result is what I really want to return to the client.
And if I change the type variable in the http.route to http instead of json, I will can't receive json format data from the client.
What shoul I do?Thanks everyone!
My Odoo version is 10,python version is 2.7.12
Here is my code
controllers.py
from odoo.http import Controller,route
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'])
def access_something(self,**kwargs):
return {"a":1,"b":2}
Test interface with requests
import requests
re = requests.get('http://192.168.1.55:8069/v1/access_something',json={"c":1},headers={'Content-Type':'application/json'})
print(re.json())
The data in re.json()
{
"jsonrpc": "2.0",
"id": null,
"result": {
"a": 1,
"b": 2
}
}
But the following result is what I want.
{
"a": 1,
"b": 2
}
I've found a way to solve this problem.
This problem arises because there is a method _json_responsein the source code JsonRequestthat we can overwrite dynamically.
In order not to interfere with the use of the original framework by others, we can pass our own specific parameters in our own decorator#http.routeby using kwargs. We construct the json dictionary we need to return to the client by determining whether the decorator has our own parameters.
Here is my codecontrollers.py
from odoo.http import Controller,route,JsonRequest
def _json_response(self, result=None, error=None):
lover = self.endpoint.routing.get('lover')
if lover == 'chun':
response = {}
if error is not None:
response['error'] = error
if result is not None:
response = result
else:
response = {
'jsonrpc': '2.0',
'id': self.jsonrequest.get('id')
}
if error is not None:
response['error'] = error
if result is not None:
response['result'] = result
if self.jsonp:
# If we use jsonp, that's mean we are called from another host
# Some browser (IE and Safari) do no allow third party cookies
# We need then to manage http sessions manually.
response['session_id'] = self.session.sid
mime = 'application/javascript'
body = "%s(%s);" % (self.jsonp, json.dumps(response),)
else:
mime = 'application/json'
body = json.dumps(response)
return Response(
body, headers=[('Content-Type', mime),
('Content-Length', len(body))])
setattr(JsonRequest,'_json_response',_json_response) #overwrite the method
class API(Controller):
#route('/v1/access_something',type='json',auth='none',csrf=False,methods=['GET'],lover='chun')
def access_something(self,**kwargs):
return {"a":1,"b":2}
The specific parameter lover='chun' is basis of our judgment.In method _json_response,we can get this parameter through self.endpoint.routing.get('lover')

Auth0 obtain access_token for unit tests in Python

I'm trying to run unit tests in Python for my flask application for routes that depend on the userID which is obtained from the access_token.
Is there a way to call the auth0 authorize API, in Python, to obtain an access_token for a user given their username and password?
If not, then what is an automated way of calling the authorize API to give it a username and password and obtain an access_token?
A code snippet would be best.
Thanks to #Jerdog, I've constructed the required piece of code:
import json
import requests
# testing user password database:
testingUsers = {
'testingUser2#funnymail.com': 'BuQ3tUS3 :jbFAL',
'testingUser3w#funnymail.com': 'y(1726854(b(-KY'
}
def getUserToken(userName):
# client id and secret come from LogIn (Test Client)! which has password enabled under "Client > Advanced > Grant Types > Tick Password"
url = 'https://YOUR_AUTH0_DOMAIN/oauth/token'
headers = {'content-type': 'application/json'}
password = testingUsers[userName]
parameter = { "client_id":"Jfjrl12w55uqcJswWmMhSm5IG2Qov8w2e",
"client_secret": "3E5ZnqLFbPUppBLQiGDjB0H2GtXaLyaD26sdk2HmHrBXQaDYE453UCUoUHmt5nWWh",
"audience": 'AUTH0_AUDIENCE',
"grant_type": "password",
"username": userName,
"password": password, "scope": "openid" }
# do the equivalent of a CURL request from https://auth0.com/docs/quickstart/backend/python/02-using#obtaining-an-access-token-for-testing
responseDICT = json.loads(requests.post(url, json=parameter, headers=headers).text)
return responseDICT['access_token']
#memoize # memoize code from: https://stackoverflow.com/a/815160
def getUserTokenHeaders(userName='testingUser2#funnymail.com'):
return { 'authorization': "Bearer " + getUserToken(userName)}
The #memoize decorator is to avoid multiple calls to get a token over many tests. The tenant has to have a default database specified for the above call to work (see this answer). It was a bit cryptic as to what the database name was supposed to be (the default_directory), but for me, with only Auth0 users, the database was Username-Password-Authentication, which seems to be the default for new accounts.
Have you looked at the https://auth0.com/docs/quickstart/backend/python/01-authorization walkthrough? The full quickstart for Python should give you a good start

How to use token authentication while testing REST API

I am trying to use test the API request by using tokens. I was able to extract the token but I struggle to find a way to use it.
This is how I get my token:
#pytest.mark.django_db
class TestUserAPI(APITestCase):
def setUp(self):
self.created_user = UserFactory()
User.objects.create_user(username='test', password='pass1234')
def test_authentification(self):
request = self.client.post('http://localhost:8000/api/v1/auth/',
{
"username": "test",
"password": "pass1234"
})
TestUserAPI.token = request.data["token"]
assert request.status_code == 200
and this is how I use it:
def test_profile(self):
request = self.client.get('http://localhost:8000/api/v1/profile/',
TokenAuthentication = 'token {}'.format(TestUserAPI.token))
assert request.status_code == status.HTTP_200_OK
It gives me 401 error. What is the correct way of using token?
The solution is simple and it's because of my inexperience with testing. The test_profile function doesn't register that a token has been asked in the test_authentification function. So I had to put both of them in the SetUp function for the token to be registered for every function in the class.
According to documentation correct header format is Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
According to official documentation the the token key should be Authentication and the token should be should be trailing Token and a white space.
In your code change token {} to Token {}.
If the problem persists try to print request headers in you view by printing request.META to check the key.

MailChimp bad request JSONParseError with Python

I am trying to hook up to MailChimp's api, in my Django application, to add an email to one of my lists. Seems pretty simple enough. I add my api key to the header of the request, with the email address and other variable in the body of the request. every time I try and connect though, I get a response status code of 400. The message says there is a JSON parsing error, and that my JSON is either formatted incorrectly, or there is missing data required for the request. I am making this same api call however with Postman, and am getting a good response back.
view function
import requests
def join_newsletter(request, email):
# hash the user's email for mailchimp's API
# m = hashlib.md5()
# c_email = email
# m.update(c_email.encode('utf-8'))
# email_hash = m.hexdigest()
api_key = 'apikey ' + settings.MAILCHIMP_API
api_endpoint = api_endpoint
data = {
"email_address": email,
"status": "subscribed"
}
header = {
'Authorization': api_key
}
r = requests.post(api_endpoint, data=data, headers=header)
message = r.content
return message

Unable to login to website through python

I am trying to login to a website in order to get some data. I have noticed that there is not form-data in the 'post' method but there is a 'request payload'. Furthermore, when I login in I cannot see anymore the login post method. Here is a screenshot of the network post login method:
When I login the next page showed is I use the following code in order to login:
import requests
urlData = 'https://b*********.dk/Account/Market'
urlLogin = 'https://b**********an.dk/
with requests.Session() as c:
urlLogin = 'https://b*************n.dk/Authorization/
c.get(urlLogin)
NetSession = c.cookies['ASP.NET_SessionId']
login_data = {
'ASP.NET_SessionId': NetSession,
'username':"A******",
'Password':"q******",
'remmemberMe': True
}
lol = c.post(urlLogin, data=login_data)
print(lol.text)
Running this code the following is outputed:
{"Processed":true,"Message":"The user name or password provided is incorrect.","NeedResetPassword":false}
When i input a wrong password the Processed value is false, while with correct credentials is true. But it deosnt login. Any idea why this could happen?
As you've already correctly noticed, the original credentials are not sent using form encoding (meaning &user=alice&password=secret), but are JSON encoded (so rather {"user":"alice", "password": "secret"}). You can also see this in the request's Content-Type header, which is application/json where (as opposed to application/x-www-form-urlencoded otherwhise).
For your custom request to work, you propably also need to send JSON-encoded data. This is documented in length in the official Documentation, so I'll just give the short version:
import json
# Build session and request body just like you already did in your question
# ...
headers = {"Content-Type": "application/json"}
lol = c.post(urlLogin, data=json.dumps(login_data), headers=headers)
print(lol.json())

Categories