In flask, I'm trying to test posting a db object with credentials. I'm getting AttributeError: 'NoneType' object has no attribute 'encode'
(at bottom) when I use generate_password_hash to simulate a password in the tested method because password is None.
In test method below, I have comments that confirm that prior to calling post
, password is not None.
Why?
-- model
class Member(db.Model, UserMixin):
__tablename__ = 'member'
id = db.Column(db.Integer, primary_key=True)
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')
email = db.Column(db.String(75), unique=True)
password = db.Column(db.String(255), nullable=False, server_default='')
email_confirmed_at = db.Column('confirmed_at', db.DateTime, default=db.func.current_timestamp())
first_name = db.Column(db.String(50), nullable=False, server_default='')
last_name = db.Column(db.String(50), nullable=False, server_default='')
def __init__(self, active, email, password, email_confirmed_at, first_name, last_name) -> None:
self.active = active
self.email = email
self.password = password
self.email_confirmed_at = email_confirmed_at
self.first_name = first_name
self.last_name = last_name
def __repr__(self) -> str:
return f'id={self.id}, name={self.first_name} {self.last_name}, active={self.active}, email={self.email}'
-- method
@auth.route('/signup', methods=['POST'])
def signup_post():
active = False
email = request.form.get('email')
email_confirmed_at = datetime.datetime.now()
first_name = request.form.get('first_name')
last_name = request.form.get('last_name')
password = request.form.get('password')
member = Member.query.filter_by(email=email).first()
if member:
flash('Email address already exists')
return redirect(url_for('auth.signup'))
member_new = Member(
active=active,
email=email,
email_confirmed_at=email_confirmed_at,
first_name=first_name,
last_name=last_name,
password=generate_password_hash(password=password, method='sha256')
)
db.session.add(member_new)
db.session.commit()
return redirect(url_for('auth.login'))
-- test fixtures
@pytest.fixture(scope='session')
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
def app():
_app = create_app()
_app.config.from_object(os.environ['TEST_SETTINGS'])
with Postgresql() as postgresql:
_app.config['SQLALCHEMY_DATABASE_URI'] = postgresql.url()
ctx = _app.app_context()
ctx.push()
yield _app
ctx.pop()
@pytest.fixture(scope='session')
def client(app):
return app.test_client()
-- test method
def test_signup_post(client):
with client:
data = {
'active':False,
'email':'michael@demo.com',
'email_confirmed_at':'2022-01-10 00:32:05.041647',
'first_name':'michael',
'last_name':'rodgers',
'password':generate_password_hash(password='123', method='sha256')
}
# assert data['password'] == 'hello' # intentional fail here to confirm password is sha256 value
# assert data['password'] # passes
response = client.post( # FAILS HERE
'/signup',
data=json.dumps(data),
content_type='application/json',
)
# data = json.loads(response.data.decode())
assert response.status_code == 201
assert 'michael@demo.com was added!' in data['message']
assert 'success' in data['status']
-- create_app
def create_app():
app = Flask(__name__)
app.config.from_object(os.environ['DEV_SETTINGS'])
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import Member
@login_manager.user_loader
def load_user(user_id):
# query for the user using user_id pk
return Member.query.get(int(user_id))
with app.app_context():
from .auth import auth as auth_bp
app.register_blueprint(auth_bp)
from .routes import routes as routes_bp
app.register_blueprint(routes_bp)
db.create_all()
return app
-- error
tests/test_endpoints.py:15:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venvn/lib/python3.9/site-packages/werkzeug/test.py:1134: in post
return self.open(*args, **kw)
venvn/lib/python3.9/site-packages/flask/testing.py:222: in open
return Client.open(
venvn/lib/python3.9/site-packages/werkzeug/test.py:1074: in open
response = self.run_wsgi_app(request.environ, buffered=buffered)
venvn/lib/python3.9/site-packages/werkzeug/test.py:945: in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
venvn/lib/python3.9/site-packages/werkzeug/test.py:1231: in run_wsgi_app
app_rv = app(environ, start_response)
venvn/lib/python3.9/site-packages/flask/app.py:2464: in __call__
return self.wsgi_app(environ, start_response)
venvn/lib/python3.9/site-packages/flask/app.py:2450: in wsgi_app
response = self.handle_exception(e)
venvn/lib/python3.9/site-packages/flask/app.py:1867: in handle_exception
reraise(exc_type, exc_value, tb)
venvn/lib/python3.9/site-packages/flask/_compat.py:39: in reraise
raise value
venvn/lib/python3.9/site-packages/flask/app.py:2447: in wsgi_app
response = self.full_dispatch_request()
venvn/lib/python3.9/site-packages/flask/app.py:1952: in full_dispatch_request
rv = self.handle_user_exception(e)
venvn/lib/python3.9/site-packages/flask/app.py:1821: in handle_user_exception
reraise(exc_type, exc_value, tb)
venvn/lib/python3.9/site-packages/flask/_compat.py:39: in reraise
raise value
venvn/lib/python3.9/site-packages/flask/app.py:1950: in full_dispatch_request
rv = self.dispatch_request()
venvn/lib/python3.9/site-packages/flask/app.py:1936: in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
myapp/auth.py:59: in signup_post
password=generate_password_hash(password=password, method='sha256')
venvn/lib/python3.9/site-packages/werkzeug/security.py:200: in generate_password_hash
h, actual_method = _hash_internal(method, salt, password)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
method = 'sha256', salt = b'eYNsNBRyTiRtGv3W', password = None # confirms password is None
def _hash_internal(method: str, salt: str, password: str) -> t.Tuple[str, str]:
"""Internal password hash helper. Supports plaintext without salt,
unsalted and salted passwords. In case salted passwords are used
hmac is used.
"""
if method == "plain":
return password, method
salt = salt.encode("utf-8")
> password = password.encode("utf-8")
E AttributeError: 'NoneType' object has no attribute 'encode'
venvn/lib/python3.9/site-packages/werkzeug/security.py:148: AttributeError