Why Flask pytest wont post data within app? - python

I'm trying to test the following app:
import os
import json
import pytest
from flask import Flask, request, jsonify
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config['TESTING'] = True
try:
os.makedirs(app.instance_path)
except OSError:
pass
#app.route('/predict', methods=['POST'])
def predict():
p = (request.get_json()) * 2
try:
return jsonify({'status': 200, 'message': 'ok', 'data': p})
except Exception as e:
return jsonify({'status': 400, 'message': str(e)}), 400
return app
#pytest.fixture
def client():
app = create_app()
app.config["TESTING"] = True
app.testing = True
with app.test_client() as client:
yield client
def test_predict(client):
response = client.post('/predict', data=json.dumps({'res': 3}))
data = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
assert data['data'] == 6
When I'm running the following command:
pytest
Im getting the following error:
FAILED test_flask_testing.py::test_predict - TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
It seems like my post request won't pass data to the app, what's wrong with my post request?
Thanks in advance!

The data is being passed, but I don't understand what you are trying to do with this line:
p = (request.get_json()) * 2
Did two small changes, so the tests now are passing:
## -20,7 +20,7 ## def create_app(test_config=None):
#app.route('/predict', methods=['POST'])
def predict():
- p = (request.get_json()) * 2
+ p = request.get_data()
try:
return jsonify({'status': 200, 'message': 'ok', 'data': p})
except Exception as e:
## -42,4 +42,4 ## def test_predict(client):
response = client.post('/predict', data=json.dumps({'res': 3}))
data = json.loads(response.get_data(as_text=True))
assert response.status_code == 200
- assert data['data'] == 6
+ assert data['data'] == '{"res": 3}'
If you are trying to receive the incoming data as json, then you need to run the post like this:
response = client.post('/predict', json={'res': 3})
Then you need to parse as json:
#app.route('/predict', methods=['POST'])
def predict():
p = request.get_json()

Related

How to mock url with Pytest parametrize and responses

I am new to unit testing in Python. I am trying to mock a response, but the url doesn't get mocked returning error that the mock is not registered and gives me a hint to use the real URL, with the real one it works, but it needs to be mocked somehow. Tried out pytest parametrize without success.
This is what I tried so far:
FAKE_HOST = "https://fake-host.com"
#pytest.mark.parametrize(
("fake_url"),
[(FAKE_HOST, "https://fake-host.com")],
)
#responses.activate
def test_item(fake_url):
responses.add(
responses.GET,
f"{fake_url}/rest/info?name=item",
status=200,
)
resp = requests.get(
"https://{fake_url}/rest/info?name=item"
)
assert resp.status_code == 200
import requests
def example2():
r = requests.get("http://httpbin.org/" + "get")
if r.status_code == 200:
response_data = r.json()
return r.status_code, response_data["url"]
else:
return r.status_code, ""
def test_get_response_success(monkeypatch):
class MockResponse(object):
def __init__(self):
self.status_code = 200
self.url = "http://httpbin.org/get"
self.headers = {"foobar": "foooooo"}
def json(self):
return {"fooaccount": "foo123", "url": "https://fake-host.com"}
def mock_get(url):
return MockResponse()
monkeypatch.setattr(requests, "get", mock_get)
assert example2() == (200, "https://fake-host.com")
Have you considered using monkepyatching?

Why database datas are being deleted after restarting flask server?

Recently, I have started flask. I found flask-sqlalchemy to work with flask. So I was using this. But I faced some problems. I am working on repl.it. When the repl goes assleep, I tried to use with the flask enabled website. But saw that no datas were showing. But before that I added a lot of datas. I can't figure out what is wrong but could you help me?
It may help you:
from flask_sqlalchemy import SQLAlchemy
import re
from flask import Flask, abort, jsonify, redirect, request
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class apikey(db.Model):
apikey = db.Column(db.String(25), primary_key=True, unique = True)
def __init__(self, apikey):
self.apikey = apikey
class shorturl(db.Model):
short_query = db.Column(db.String(15), primary_key=True, unique = True)
original = db.Column(db.String(1000))
visits = db.Column(db.Integer)
def __init__(self, short_query,original, visits):
self.short_query = short_query
self.original = original
self.visits = visits
def url_valid(url):
return re.match(regex, url) is not None
def bad_request(message):
response = jsonify({'message': message})
response.status_code = 400
return response
def errreq(message):
response = jsonify({'message': message})
response.status_code = 404
return response
#app.route('/')
def show_all():
return redirect("https://www.cburst.ml", code=301)
#app.route('/addapi', methods=['GET'])
def addapi():
if request.args.get('apikey') is not None:
api_key = request.args.get('apikey')
apiadd = apikey(apikey=api_key)
db.session.add(apiadd)
db.session.commit()
return jsonify({'message':"Done"}), 200
else:
return bad_request("Nothing Provided")
#app.route('/add', methods=['POST'])
def add():
if not request.json:
return bad_request('Url must be provided in json format.')
if "original" not in request.json :
return bad_request('Url parameter not found.')
if "short_query" not in request.json:
return bad_request('Url parameter not found.')
original = request.json['original']
short = request.json['short_query']
if shorturl.query.filter_by(short_query = short).first() is not None:
return bad_request("Already Exists")
visits = 0
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short, original=original, visits=visits)
shortened_url = short
db.session.add(url_db)
db.session.commit()
return jsonify({'link': shortened_url}), 201
#app.route('/add', methods=['GET'])
def add_get():
if request.args.get('apikey') is not None and request.args.get('original') is not None and request.args.get('short_query') is not None:
api_key = request.args.get('apikey')
original = request.args.get('original')
short_query = request.args.get('short_query')
if apikey.query.filter_by(apikey = api_key).first() is None:
return errreq("Invalid API Key")
else:
if shorturl.query.filter_by(short_query=short_query).first() is not None:
return errreq("Already Exists")
else:
if original[:4] != 'http':
original = 'http://' + original
if not url_valid(original):
return bad_request('Provided url is not valid.')
url_db = shorturl(
short_query=short_query, original=original, visits=0)
db.session.add(url_db)
db.session.commit()
return jsonify({'link': short_query}), 200
else:
return bad_request("Nothing Provided")
#app.route('/view/<alias>', methods=['GET'])
def get_viewcount(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
else:
return jsonify({'visits':shorturl.query.filter_by(short_query=alias).first().visits}),200
#app.route('/<alias>', methods=['GET'])
def get_shortened(alias):
if shorturl.query.filter_by(short_query=alias).first() is None:
return bad_request('Unknown alias.')
visits = shorturl.query.filter_by(short_query=alias).first().visits
url_db = shorturl.query.filter_by(short_query=alias).first()
url_db.visits = visits + 1
db.session.commit()
url = shorturl.query.filter_by(short_query=alias).first().original
return redirect(url, code=302)
# From https://stackoverflow.com/questions/7160737/python-how-to-validate-a-url-in-python-malformed-or-not#7160778
# Slightly modified to not use ftp.
regex = re.compile(
r'^(?:http)s?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'
r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?'
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
if __name__ == '__main__':
'''
if os.path.exists('./db.sqlite3'):
app.run(host="0.0.0.0", port=8000, debug=False)
else: '''
db.create_all()
app.run(host="0.0.0.0", port=8000, debug=False)
Here if I add api key in /appapi with key in query string, I can add the key in the database. But the key added in the database does not work after a couple of hours.
Thanks in advance.
I figured this out. It is because I have added db.create_all() inside the main function. Removing this just works.

Plaid API quickstart project error http://127.0.0.1:5000/get_access_token

I am trying to get started with the plaid API. I created my account to get the API keys and I have the quickstart project. I put my keys in the code(they are not applied in the code below) and when I run it I use the sandbox credentials. Unfortunately after the log in succeeds I always receive the same error when trying to receive the access token:
HTTP500: SERVER ERROR - The server encountered an unexpected condition
that prevented it from fulfilling the request.(XHR)POST -
http://127.0.0.1:5000/get_access_token
Here is the code:
import os
import datetime
import plaid
from flask import Flask
from flask import render_template
from flask import request
from flask import jsonify
app = Flask(__name__)
# Fill in your Plaid API keys - https://dashboard.plaid.com/account/keys
PLAID_CLIENT_ID = os.getenv('PLAID_CLIENT_ID')
PLAID_SECRET = os.getenv('PLAID_SECRET')
PLAID_PUBLIC_KEY = os.getenv('PLAID_PUBLIC_KEY')
# Use 'sandbox' to test with Plaid's Sandbox environment (username:
user_good,
# password: pass_good)
# Use `development` to test with live users and credentials and `production`
# to go live
PLAID_ENV = os.getenv('PLAID_ENV', 'sandbox')
client = plaid.Client(client_id = PLAID_CLIENT_ID, secret=PLAID_SECRET,
public_key=PLAID_PUBLIC_KEY, environment=PLAID_ENV)
#app.route("/")
def index():
return render_template('index.ejs', plaid_public_key=PLAID_PUBLIC_KEY,
plaid_environment=PLAID_ENV)
access_token = None
public_token = None
#app.route("/get_access_token", methods=['POST'])
def get_access_token():
global access_token
public_token = request.form['public_token']
exchange_response = client.Item.public_token.exchange(public_token)
print ('public token: ' + public_token)
print ('access token: ' + exchange_response['access_token'])
print ('item ID: ' + exchange_response['item_id'])
access_token = exchange_response['access_token']
return jsonify(exchange_response)
#app.route("/accounts", methods=['GET'])
def accounts():
global access_token
accounts = client.Auth.get(access_token)
return jsonify(accounts)
#app.route("/item", methods=['GET', 'POST'])
def item():
global access_token
item_response = client.Item.get(access_token)
institution_response = client.Institutions.get_by_id(item_response['item']
['institution_id'])
return jsonify({'item': item_response['item'], 'institution':
institution_response['institution']})
#app.route("/transactions", methods=['GET', 'POST'])
def transactions():
global access_token
# Pull transactions for the last 30 days
start_date = "{:%Y-%m-%d}".format(datetime.datetime.now() +
datetime.timedelta(-30))
end_date = "{:%Y-%m-%d}".format(datetime.datetime.now())
try:
response = client.Transactions.get(access_token, start_date, end_date)
return jsonify(response)
except plaid.errors.PlaidError as e:
return jsonify({'error': {'error_code': e.code, 'error_message':
str(e)}})
#app.route("/create_public_token", methods=['GET'])
def create_public_token():
global access_token
# Create a one-time use public_token for the Item. This public_token can
be used to
# initialize Link in update mode for the user.
response = client.Item.public_token.create(access_token)
return jsonify(response)
if __name__ == "__main__":
app.run(port=os.getenv('PORT', 5000))
Update your code like
PLAID_CLIENT_ID = 'client_id'
PLAID_SECRET = 'secret'
PLAID_PUBLIC_KEY = 'key'
PLAID_ENV = 'sandbox'
the problem was solved by putting the credentials to the client.py-file which is being created while you install plaid

python - Flask basic auth testing with pytest

I'm currently using Flask-HTTPAuth for basic authentication within my project. I've tested it by hand using curl and base64 tokens and it does indeed work. However I'm running into problems creating tests proving it works. This is my current test and it always turns back 401:
class TestLoginApi
def setup(self):
myapp.app.config.from_object("config.TestingConfig")
self.app = myapp.app.test_client()
client = MongoClient(myapp.app.config['DATABASE_URL'])
db = client.get_default_database()
assert list(db.collection_names()) == []
db.users.insert_one({'name': 'testuser', 'password': 'testpassword'})
def teardown(self):
client = MongoClient(myapp.app.config['DATABASE_URL'])
client.drop_database(client.get_default_database())
def test_not_logged_in(self):
rv = self.app.get('/api/v1/login/')
assert rv.status_code == 401
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
I've also tried changing the following:
def test_correct_password(self):
d = Headers()
d.add('Authorization', 'Basic dGVzdHVzZXI6dGVzdHBhc3N3b3Jk')
rv = self.app.get('/api/v1/login/', headers=d,
content_type='application/json')
assert rv.status_code == 200
to this with no success:
def test_correct_password(self):
rv = self.app.get('/api/v1/login/', headers={"Authorization": "Basic {user}".format(user=base64.b64encode(b"testuser:testpassword"))})
assert rv.status_code == 200
You can test authorization like this:
import base64
valid_credentials = base64.b64encode(b"testuser:testpassword").decode("utf-8")
response = self.app.get(
"/api/v1/login/",
headers={"Authorization": "Basic " + valid_credentials}
)
assert response.status_code == 200
I think you would enjoy my github project, which has a tons of different tests for Flask-HTTPAuth.

Confused on proper implementation of Flask blueprints and socketio

I have a Flask app that I have restructured to leverage blueprints. The application runs and everything seems to be going ok except for SocketIO and my socketio.on events. I never see SocketIO and the web socket attempt to connect or disconnect in Chrome debugger all it ever says is pending. I have checked the Flask SocketIO Chat example here, and got some ideas. I moved all my events back into my views.py. I can't seem to figure out why my sockio.on events are not getting called or firing off. Here is my code.
app.py
from factory import create_app
from flask.ext.socketio import SocketIO
app = create_app()
socketio = SocketIO(app)
factory.py
import logging
from logging.handlers import RotatingFileHandler
from flask import Flask
# from flask.ext.socketio import SocketIO
from flask.ext.login import LoginManager
import os
from celery import Celery
lm = LoginManager()
# socketio = SocketIO()
lm.login_view = 'main.login'
lm.session_protection = 'strong'
def create_app():
app = Flask(__name__)
app.clients = {}
app.config.from_object(os.environ.get('APP_CONFIG')) # export APP_CONFIG=settings.Development
lm.init_app(app)
from project.main import main as main_blueprint
app.register_blueprint(main_blueprint)
# socketio.init_app(app)
# print app.config['LOGPATH']
if not os.path.isdir(app.config['LOGPATH']):
print 'Log dir not found'
os.makedirs(app.config['LOGPATH'])
if not os.path.isdir(app.config['UPLOAD_FOLDER']):
print 'Upload dir not found'
os.makedirs(app.config['UPLOAD_FOLDER'])
# See Flask error handling for more info on logging
file_handler = RotatingFileHandler(app.config['LOGPATH'] + 'my.log', maxBytes=1024 * 1024 * 10, backupCount=20)
file_handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s -%(module)s - %(lineno)d - %(message)s"))
app.logger.setLevel(logging.DEBUG)
app.logger.addHandler(file_handler)
app.logger.debug('Starting My Application')
# app.logger.debug(socketio)
return app
def make_celery(app=None):
app = app or create_app()
celery = Celery(app.import_name, broker=app.config['CELERY_BROKER_URL'])
# celery.conf.update(app.config)
celery.config_from_envvar('APP_CONFIG')
TaskBase = celery.Task
class ContextTask(TaskBase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return TaskBase.__call__(self, *args, **kwargs)
celery.Task = ContextTask
return celery
main/_init_.py
from flask import Blueprint
main = Blueprint('main', __name__, template_folder='templates', static_folder='static')
import views
# import events
main/views.py
from celery import chain
from flask import render_template, request, url_for, jsonify, current_app, session
from . import main
from flask.ext.socketio import emit, disconnect
from ..app import socketio
from flask.json import dumps
from werkzeug.utils import secure_filename, redirect
from flask.ext.login import login_required, login_user, logout_user
from uuid import uuid4
from project.tasks import *
#main.route('/')
# #login_required
def index():
event_count = 0
current_app.logger.debug('Loaded homepage')
current_app.logger.debug(socketio)
return render_template('index.html',
event_count=event_count,
http_id=session['http_id'])
# #login_required
#main.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
websocket_session_id = request.form.get('session')
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename))
chain(
parse.s(filename, session['http_id']),
create_sphere.s(session['http_id'], websocket_session_id, url_for('main.event', _external=True))
)()
# parse.delay(filename, session['http_id'])
# (filename, session['http_id']),
# link=create_sphere.s(session['http_id'], websocket_session_id, url_for('main.event', _external=True))
# )
# parse.apply_async(chain)
return jsonify({'status': 'processing CSV'})
else:
return jsonify({'status': 'wrong file type'})
if request.method == 'GET':
return render_template('upload.html')
#main.route('/clients', methods=['GET'])
def clients():
return jsonify({'clients': current_app.clients.keys()})
#main.route('/event/', methods=['POST'])
def event():
print '\n'
print 'request =', request
print 'request.json =', request.json
current_app.logger.debug('task: {t}'.format(t=request.json['task']))
current_app.logger.debug('success: {s}'.format(s=request.json['success']))
websocket_id = request.json['websocket_id']
current_app.logger.debug(websocket_id)
if request.json['task'] == 'task_sphere' and request.json['success']:
current_app.logger.debug('successfully parsed CSV data')
current_app.logger.debug(request)
websocket_id = request.json['websocket_id']
http_id = request.json['http_id']
current_app.logger.debug(websocket_id)
current_app.logger.debug(http_id)
# try:
conn = pymongo.MongoClient(current_app.config['MONGO_URL'])
db = conn.events
collection = db['%s' % http_id]
current_app.logger.debug('Collection: {c}'.format(c=collection))
ns = current_app.clients.get(websocket_id)
if ns:
current_app.logger.debug(ns)
nodes = dumps(list(collection.find({'type' : 'node'})))
edges = dumps(list(collection.find({'type' : 'edge'})))
if nodes:
ns.emit('insert_nodes', nodes)
if edges:
ns.emit('insert_edges', edges)
# ns.emit('insert_data', dumps(list(collection.find())))
# return 'ok'
# except:
# print 'Could not connect to MongoDB: %s'
# return 'ok'
return 'ok'
# #lm.user_loader
# def load_user(username):
# u = main.config['USERS_COLLECTION'].find_one({"_id": username})
# if not u:
# return None
# return User(u['_id'])
#main.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Creds'
else:
session['logged_in'] = True
session['http_id'] = str(uuid4())
return redirect(url_for('main.login'))
return render_template('login.html', error=error)
#main.route('/logout')
#login_required
def logout():
logout_user()
return redirect(url_for('main.login'))
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in current_app.config['ALLOWED_EXTENSIONS']
##### Put events here for the time being #####
#socketio.on('status', namespace='/events')
def events_message(message):
current_app.logger.debug(message['status'])
print 'socketio.on: status'
emit('status', {'status': message['status']})
#socketio.on('disconnect request', namespace='/events')
def disconnect_request():
print 'socketio.on: disconnect request'
current_app.logger.debug('DISCONNECT REQUEST')
emit('status', {'status': 'Disconnected!'})
disconnect()
#socketio.on('connect', namespace='/events')
def events_connect():
print 'socketio.on: connect'
websocket_id = str(uuid4())
session['websocket_id'] = websocket_id
current_app.logger.debug(websocket_id)
current_app.clients[websocket_id] = request.namespace
emit('websocket_id', {'websocket_id': websocket_id})
#socketio.on('disconnect', namespace='/events')
def events_disconnect():
print 'socketio.on: diconnect'
current_app.logger.debug('DISCONNECT')
del current_app.clients[session['websocket_id']]
print('Client %s disconnected' % session['websocket_id'])
static/js/application.js
var namespace = '/events'; // change to an empty string to use the global namespace
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
var jared;
var edges;
var nodes;
socket.on('connect', function () {
console.log('socketio: connect');
});
socket.on('disconnect', function () {
console.log('socketio: disconnect');
$('#websocket_id').text('not available');
});
socket.on('websocket_id', function (msg) {
console.log('updating html elements with correct session', msg.websocket_id);
$('input[name="session"]').val(msg.websocket_id);
$('#websocket_id').text(msg.websocket_id);
});
socket.on('insert_nodes', function (msg) {
//console.log(msg);
//jared = msg;
$('#myModal').modal('hide');
nodes = JSON.parse(msg);
console.log('here are the nodes', nodes);
var pcGeometry = new THREE.Geometry();
for (var i = 0; i < nodes.length; i++) {
var position = nodes[i].position;
//console.log(position);
vector = new THREE.Vector3(position.x, position.y, position.z);
//console.log(vector);
pcGeometry.vertices.push(vector);
}
//console.log(geometry);
pcMat = new THREE.PointCloudMaterial();
pcMat.size = 10;
pcMat.transparent = true;
pcMat.blending = THREE.AdditiveBlending;
pcMat.color = new THREE.Color(0x5555ff);
pc = new THREE.PointCloud(pcGeometry, pcMat);
pc.sizeAttenuation = true;
webGLScene.add(pc);
});
socket.on('insert_edges', function (msg) {
function getById(id, myArray) {
return myArray.filter(function (obj) {
if (obj._id.$oid == id) {
return obj
}
})[0]
}
edges = JSON.parse(msg);
console.log('here are the edges', edges);
var material = new THREE.LineBasicMaterial({
opacity: .3,
blending: THREE.AdditiveBlending,
transparent: true
});
//geometry.vertices.push(start);
for (var i = 0; i < edges.length; i++) {
var start = edges[i].start.$oid;
var start_pos = getById(start, nodes);
var start_vec = new THREE.Vector3(start_pos.position.x, start_pos.position.y, start_pos.position.z);
var end = edges[i].end.$oid;
var end_pos = getById(end, nodes);
var end_vec = new THREE.Vector3(end_pos.position.x, end_pos.position.y, end_pos.position.z);
var geometry = new THREE.Geometry();
geometry.vertices.push(start_vec);
geometry.vertices.push(end_vec);
var line = new THREE.Line(geometry, material);
webGLScene.add(line);
}
});
socket.on('status', function (msg) {
console.log('status', msg);
});

Categories

Resources