Creating a test for a flask app using nose2 - python

I have a flask app that works (tested with postman).
I am now writing unittest for it as I am trying with Travis and CI on my repository, I use nose2.
The test class I wrote is this:
import unittest
import requests
from myapp import app
class ProjectTests(unittest.TestCase):
############################
#### setup and teardown ####
############################
# executed prior to each test
def setUp(self):
app.config["TESTING"] = True
app.config["DEBUG"] = False
self.app = app.test_client()
self.assertEquals(app.debug, False)
# executed after each test
def tearDown(self):
pass
########################
#### helper methods ####
########################
###############
#### tests ####
###############
def test_main_page(self):
response = self.app.get("/", follow_redirects=True)
# self.assertIn(b"Hello World!", response.data)
# response = app.test_client().get('/')
assert response.status_code == 200
# print(response.data)
assert response.data == b"Hello, world!"
def test_register(self):
# import requests
url = "http://127.0.0.1:5000/register"
payload = '{\n\t"username": "bruno",\n\t"password": "asdf"\n}\n'
headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, data=payload)
# print(response.text.encode("utf8"))
print(response.json()["message"])
assert response.status_code == 400 # user already exists
if __name__ == "__main__":
unittest.main()
If I run the app and then I copy and paste the test_register method, i.e.
import requests
url = "http://127.0.0.1:5000/register"
payload = '{\n\t"username": "bruno",\n\t"password": "asdf"\n}\n'
headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, data=payload)
# print(response.text.encode("utf8"))
print(response.json()["message"])
everything is fine. It fails if I run nose2 and I get a connection refused error.
If I comment the test_register method and nose2 runs fine. I reckon I am implementing the two tests differently.
Any help on how to fix this?

Related

Launching a workflow template with extra vars

I'm trying to launch a workflow template from a python script in Ansible AWX, my script looks like this
#!/usr/bin/env python
import requests
import json
from requests.auth import HTTPBasicAuth
import base64
import os
def main():
URL = 'https://myawx/api/v2/'
USER = 'username'
PASSWORD = 'password'
session = object
session = requests.Session()
session.auth = (USER, PASSWORD)
session.verify = False
myvars={
"test": "test"
}
AWX_JOB_TEMPLATES_API="https://myawx.com/api/v2/workflow_job_templates/10/launch/"
response = session.post(url=AWX_JOB_TEMPLATES_API,json=myvars, verify=False)
print(response)
if __name__ == '__main__':
main()
This launches the workflow fine, but it doesn't include the extra vars from the net_vars variable. My workflow_job_template has "Prompt on launch" enabled for the EXTRA VARIABLES field from the frontend of AWX.
Is it possible to launch a workflow with extra vars?
If you want to use extra vars via REST API, myvars needs to have extra_vars key.
For example, here's like.
#!/usr/bin/env python
import requests
def main():
url = "http://awx_server/api/v2/job_templates/14/launch/"
username = "admin"
passwd = "secret"
headers = {
"Content-Type": "application/json",
}
data = {}
data['extra_vars'] = {
"extra_string": "test message"
}
req = requests.post(url,
headers=headers,
auth=(username, passwd),
json=data,
verify=False)
print(req.status_code)
print(req.text)
if __name__ == "__main__":
main()
When the above sample, set test message to extra_string variable.
The extra_string is to be given as extra vars via REST API.

filter out json with json.loads(r.text)

I am trying to fetch rest api from XYZ provider inside python/flask webApp I used this as example/guide https://help.parsehub.com/hc/en-us/articles/217751808-API-Tutorial-How-to-get-run-data-using-Python-Flask
I dont have api key but auth token so this is code I am using and returns alldata
`from flask import Flask, render_template
import requests
import json
app = Flask(__name__)
#app.route('/')
def homepage():
headers = {
"Accept": "application/json",
"Authorization": "Bearer MYKEY"
}
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers)
return json.loads(r.text)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
`
and this return all json data like this
json image
Id like to filter is to display only one part "domain" and "destination" but when adding code like this
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers)
return json.loads(r.text)['destination']
I keep getting "Key error "destination" " or whatever I put in there
error
Your json returns data which is an array that contains destination. Assuming you want the first destination, try
return json.loads(r.text)['data'][0]['destination']
Or it coulde be written as:
results = json.loads(r.text)
return results['data'][0]['destination']
It seems like 'destination' is stored under 'data' in the JSON response. Also, you should just call r.json() instead of json.loads(r.text). You should also jsnoify your response.
from flask import Flask, render_template, jsonify
import requests
app = Flask(__name__)
#app.route('/')
def homepage():
headers = {
"Accept": "application/json",
"Authorization": "Bearer MYKEY"
}
r = requests.get(
'https://st1.example.com/restapi/domains/', headers=headers
)
destination = r.json()['data'][0]['destination']
return jsonify(destination), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

AWS Chalice, can't get image from POST request

I'm trying to invoke my sagemaker model using aws chalice, a lambda function, and an API Gateaway.
I'm attempting to send the image over POST request but I'm having problem receiving it on the lambda function.
My code looks like:
from chalice import Chalice
from chalice import BadRequestError
import base64
import os
import boto3
import ast
import json
app = Chalice(app_name='foo')
app.debug = True
#app.route('/', methods=['POST'], content_types=['application/json'])
def index():
body = ''
try:
body = app.current_request.json_body # <- I suspect this is the problem
return {'response': body}
except Exception as e:
return {'error': str(e)}
It's just returning
<Response [200]> {'error': 'BadRequestError: Error Parsing JSON'}
As I mentioned before, my end goal is to receive my image and make a sagemaker request with it. But I just can't seem to read the image.
My python test client looks like this:
import base64, requests, json
def test():
url = 'api_url_from_chalice'
body = ''
with open('b1.jpg', 'rb') as image:
f = image.read()
body = base64.b64encode(f)
payload = {'data': body}
headers = {'Content-Type': 'application/json'}
r = requests.post(url, data=payload, headers=headers)
print(r)
r = r.json()
# r = r['response']
print(r)
test()
Please help me, I spent way to much time trying to figure this out
That error message is because you're not sending a JSON body over to your Chalice app. One way to check this is by using the .raw_body property to confirm:
#app.route('/', methods=['POST'], content_types=['application/json'])
def index():
body = ''
try:
#body = app.current_request.json_body # <- I suspect this is the problem
return {'response': app.current_request.raw_body.decode()}
except Exception as e:
return {'error': str(e)}
You'll see that the body is form-encoded and not JSON.
$ python client.py
<Response [200]>
{'response': 'data=c2FkZmFzZGZhc2RmYQo%3D'}
To fix this, you can use the json parameter in the requests.post() call:
r = requests.post(url, json=payload, headers=headers)
We can then confirm we're getting a JSON body in your chalice app:
$ python client.py
<Response [200]>
{'response': '{"data": "c2FkZmFzZGZhc2RmYQo="}'}
So I was able to figure it out with the help of an aws engineer (i got lucky I suppose). I'm including the complete lambda function. Nothing changed on the client.
from chalice import Chalice
from chalice import BadRequestError
import base64
import os
import boto3
import ast
import json
import sys
from chalice import Chalice
if sys.version_info[0] == 3:
# Python 3 imports.
from urllib.parse import urlparse, parse_qs
else:
# Python 2 imports.
from urlparse import urlparse, parse_qs
app = Chalice(app_name='app_name')
app.debug = True
#app.route('/', methods=['POST'])
def index():
parsed = parse_qs(app.current_request.raw_body.decode())
body = parsed['data'][0]
print(type(body))
try:
body = base64.b64decode(body)
body = bytearray(body)
except e:
return {'error': str(e)}
endpoint = "object-detection-endpoint_name"
runtime = boto3.Session().client(service_name='sagemaker-runtime', region_name='us-east-2')
response = runtime.invoke_endpoint(EndpointName=endpoint, ContentType='image/jpeg', Body=body)
print(response)
results = response['Body'].read().decode("utf-8")
results = results['predictions']
results = json.loads(results)
results = results['predictions']
return {'result': results}

Problems while making requests to a flask restful app

I have the following flask api, which just returns echo of its input:
from flask import Flask
from flask_restful import Resource, Api
app = Flask(__name__)
api = Api(app)
class query(Resource):
def get(self, a_string):
return{
'original': a_string,
'echo': a_string
}
api.add_resource(query,'/echo/<a_string>')
if __name__ == '__main__':
app.run()
Then, when I try to use python requests to make queries to my api:
import json
def query(text):
payload = {'echo': str(text)}
headers = {'content-type': 'application/x-www-form-urlencoded'}
r = requests.request("POST", 'http://127.0.0.1:5000', data=payload, headers=headers)
print(r)
#data = json.loads(r.text)
#return data
query('hi')
I keep getting:
<Response [404]>
Any idea of how to fix this issue? Interestingly when I go to my browser and do:
http://127.0.0.1:5000/echo/hi
I get:
{"original": "hi", "echo": "hi"}
But sending a POST to / with a payload of {"echo": whatever} is not at all the same as sending a GET to /echo/whatever. Your API expects the latter.
def query(text):
r = requests.get("http://127.0.0.1:5000/echo/{}".format(text))
Or, change your API so it does expect that:
class query(Resource):
def post(self):
a_string = request.form["echo"]
return {
'original': a_string,
'echo': a_string
}
api.add_resource(query, '/')

How do I simulate an AJAX request with Flask test client?

Testing Flask applications is done with:
# main.py
from flask import Flask, request
app = flask.Flask(__name__)
#app.route('/')
def index():
s = 'Hello world!', 'AJAX Request: {0}'.format(request.is_xhr)
print s
return s
if __name__ == '__main__':
app.run()
Then here is my test script:
# test_script.py
import main
import unittest
class Case(unittest.TestCase):
def test_index():
tester = app.test_client()
rv = tester.get('/')
assert 'Hello world!' in rv.data
if __name__ == '__main__':
unittest.main()
In the test output, I'll get:
Hello world! AJAX Request: False
Question
How do I test my app with AJAX requests?
Try this:-
def test_index():
tester = app.test_client()
response = tester.get('/', headers=[('X-Requested-With', 'XMLHttpRequest')])
assert 'Hello world!' in response.data
import json
def test_index():
data = json.dumps({})
client = app.test_client()
headers = {
'Content-Type': 'application/json',
}
response = client.post('/', data=data, headers=headers)
data = json.loads(response.data)
assert data
`

Categories

Resources