mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-17 02:32:32 -05:00
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,6 +2,8 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
|
venv/
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ from flask_marshmallow import Marshmallow
|
|||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
from config import configs
|
from config import configs
|
||||||
from utils import logging
|
from utils import logging
|
||||||
|
from notify_client import NotifyAPIClient
|
||||||
|
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
ma = Marshmallow()
|
ma = Marshmallow()
|
||||||
|
notify_alpha_client = NotifyAPIClient()
|
||||||
|
|
||||||
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
|
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
|
||||||
|
|
||||||
@@ -26,17 +28,20 @@ def create_app(config_name, config_overrides=None):
|
|||||||
ma.init_app(application)
|
ma.init_app(application)
|
||||||
init_app(application, config_overrides)
|
init_app(application, config_overrides)
|
||||||
logging.init_app(application)
|
logging.init_app(application)
|
||||||
|
notify_alpha_client.init_app(application)
|
||||||
|
|
||||||
from app.service.rest import service as service_blueprint
|
from app.service.rest import service as service_blueprint
|
||||||
from app.user.rest import user as user_blueprint
|
from app.user.rest import user as user_blueprint
|
||||||
from app.template.rest import template as template_blueprint
|
from app.template.rest import template as template_blueprint
|
||||||
from app.status.healthcheck import status as status_blueprint
|
from app.status.healthcheck import status as status_blueprint
|
||||||
from app.job.rest import job as job_blueprint
|
from app.job.rest import job as job_blueprint
|
||||||
|
from app.notifications.rest import notifications as notifications_blueprint
|
||||||
|
|
||||||
application.register_blueprint(service_blueprint, url_prefix='/service')
|
application.register_blueprint(service_blueprint, url_prefix='/service')
|
||||||
application.register_blueprint(user_blueprint, url_prefix='/user')
|
application.register_blueprint(user_blueprint, url_prefix='/user')
|
||||||
application.register_blueprint(template_blueprint, url_prefix="/template")
|
application.register_blueprint(template_blueprint, url_prefix="/template")
|
||||||
application.register_blueprint(status_blueprint, url_prefix='/status')
|
application.register_blueprint(status_blueprint, url_prefix='/status')
|
||||||
|
application.register_blueprint(notifications_blueprint, url_prefix='/notifications')
|
||||||
application.register_blueprint(job_blueprint)
|
application.register_blueprint(job_blueprint)
|
||||||
|
|
||||||
return application
|
return application
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ def requires_auth():
|
|||||||
try:
|
try:
|
||||||
auth_token = auth_header[7:]
|
auth_token = auth_header[7:]
|
||||||
api_client = fetch_client(get_token_issuer(auth_token))
|
api_client = fetch_client(get_token_issuer(auth_token))
|
||||||
|
|
||||||
if api_client is None:
|
if api_client is None:
|
||||||
authentication_response("Invalid credentials", 403)
|
authentication_response("Invalid credentials", 403)
|
||||||
|
|
||||||
|
|||||||
0
app/notifications/__init__.py
Normal file
0
app/notifications/__init__.py
Normal file
73
app/notifications/rest.py
Normal file
73
app/notifications/rest.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
jsonify,
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
from app import notify_alpha_client
|
||||||
|
import re
|
||||||
|
|
||||||
|
mobile_regex = re.compile("^\\+44[\\d]{10}$")
|
||||||
|
|
||||||
|
notifications = Blueprint('notifications', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@notifications.route('/<notification_id>', methods=['GET'])
|
||||||
|
def get_notifications(notification_id):
|
||||||
|
return jsonify(notify_alpha_client.fetch_notification_by_id(notification_id)), 200
|
||||||
|
|
||||||
|
|
||||||
|
@notifications.route('/sms', methods=['POST'])
|
||||||
|
def create_sms_notification():
|
||||||
|
notification = request.get_json()['notification']
|
||||||
|
|
||||||
|
errors = {}
|
||||||
|
to_errors = validate_to(notification)
|
||||||
|
message_errors = validate_message(notification)
|
||||||
|
|
||||||
|
if to_errors:
|
||||||
|
errors.update(to_errors)
|
||||||
|
if message_errors:
|
||||||
|
errors.update(message_errors)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
return jsonify(result="error", message=errors), 400
|
||||||
|
|
||||||
|
return jsonify(notify_alpha_client.send_sms(mobile_number=notification['to'], message=notification['message'])), 200
|
||||||
|
|
||||||
|
|
||||||
|
@notifications.route('/email', methods=['POST'])
|
||||||
|
def create_email_notification():
|
||||||
|
return jsonify(id=123)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_to(json_body):
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if 'to' not in json_body:
|
||||||
|
errors.append('required')
|
||||||
|
else:
|
||||||
|
if not mobile_regex.match(json_body['to']):
|
||||||
|
errors.append('invalid phone number, must be of format +441234123123')
|
||||||
|
if errors:
|
||||||
|
return {
|
||||||
|
"to": errors
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def validate_message(json_body):
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
if 'message' not in json_body:
|
||||||
|
errors.append('required')
|
||||||
|
else:
|
||||||
|
message_length = len(json_body['message'])
|
||||||
|
if message_length < 1 or message_length > 160:
|
||||||
|
errors.append('Invalid length. [1 - 160]')
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
return {
|
||||||
|
"message": errors
|
||||||
|
}
|
||||||
|
return None
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
NOTIFY_LOG_LEVEL = 'DEBUG'
|
NOTIFY_LOG_LEVEL = 'DEBUG'
|
||||||
@@ -6,6 +9,8 @@ class Config(object):
|
|||||||
SQLALCHEMY_COMMIT_ON_TEARDOWN = False
|
SQLALCHEMY_COMMIT_ON_TEARDOWN = False
|
||||||
SQLALCHEMY_RECORD_QUERIES = True
|
SQLALCHEMY_RECORD_QUERIES = True
|
||||||
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/notification_api'
|
SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/notification_api'
|
||||||
|
NOTIFY_DATA_API_URL = os.getenv('NOTIFY_API_URL', "http://localhost:6001")
|
||||||
|
NOTIFY_DATA_API_AUTH_TOKEN = os.getenv('NOTIFY_API_TOKEN', "dev-token")
|
||||||
ADMIN_CLIENT_USER_NAME = None
|
ADMIN_CLIENT_USER_NAME = None
|
||||||
ADMIN_CLIENT_SECRET = None
|
ADMIN_CLIENT_SECRET = None
|
||||||
|
|
||||||
|
|||||||
@@ -16,3 +16,5 @@ credstash==1.8.0
|
|||||||
git+https://github.com/alphagov/notifications-python-client.git@0.1.5#egg=notifications-python-client==0.1.5
|
git+https://github.com/alphagov/notifications-python-client.git@0.1.5#egg=notifications-python-client==0.1.5
|
||||||
|
|
||||||
git+https://github.com/alphagov/notifications-utils.git@0.0.3#egg=notifications-utils==0.0.3
|
git+https://github.com/alphagov/notifications-utils.git@0.0.3#egg=notifications-utils==0.0.3
|
||||||
|
|
||||||
|
git+https://github.com/alphagov/notify-api-client.git@0.1.6#egg=notify-api-client==0.1.6
|
||||||
|
|||||||
@@ -30,5 +30,5 @@ display_result $? 1 "Code style check"
|
|||||||
#display_result $? 2 "Code coverage"
|
#display_result $? 2 "Code coverage"
|
||||||
|
|
||||||
|
|
||||||
py.test -v
|
py.test -v tests/
|
||||||
display_result $? 3 "Unit tests"
|
display_result $? 3 "Unit tests"
|
||||||
|
|||||||
0
tests/app/notifications/__init__.py
Normal file
0
tests/app/notifications/__init__.py
Normal file
298
tests/app/notifications/test_rest.py
Normal file
298
tests/app/notifications/test_rest.py
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
from tests import create_authorization_header
|
||||||
|
from flask import url_for, json
|
||||||
|
from app import notify_alpha_client
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notifications(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.fetch_notification_by_id',
|
||||||
|
return_value={
|
||||||
|
'notifications': [
|
||||||
|
{
|
||||||
|
'id': 'my_id',
|
||||||
|
'notification': 'some notify'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
path=url_for('notifications.get_notifications', notification_id=123),
|
||||||
|
method='GET')
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
url_for('notifications.get_notifications', notification_id=123),
|
||||||
|
headers=[auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert len(json_resp['notifications']) == 1
|
||||||
|
assert json_resp['notifications'][0]['id'] == 'my_id'
|
||||||
|
assert json_resp['notifications'][0]['notification'] == 'some notify'
|
||||||
|
notify_alpha_client.fetch_notification_by_id.assert_called_with("123")
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_notifications_empty_result(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.fetch_notification_by_id',
|
||||||
|
return_value={
|
||||||
|
'notifications': [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
path=url_for('notifications.get_notifications', notification_id=123),
|
||||||
|
method='GET')
|
||||||
|
|
||||||
|
response = client.get(
|
||||||
|
url_for('notifications.get_notifications', notification_id=123),
|
||||||
|
headers=[auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert len(json_resp['notifications']) == 0
|
||||||
|
notify_alpha_client.fetch_notification_by_id.assert_called_with("123")
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_reject_if_no_phone_numbers(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value='success'
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'message': "my message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
print(json_resp)
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert json_resp['result'] == 'error'
|
||||||
|
assert len(json_resp['message']) == 1
|
||||||
|
assert len(json_resp['message']['to']) == 1
|
||||||
|
assert json_resp['message']['to'][0] == 'required'
|
||||||
|
assert not notify_alpha_client.send_sms.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_reject_bad_phone_numbers(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value='success'
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'to': 'invalid',
|
||||||
|
'message': "my message"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
print(json_resp)
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert json_resp['result'] == 'error'
|
||||||
|
assert len(json_resp['message']) == 1
|
||||||
|
assert len(json_resp['message']['to']) == 1
|
||||||
|
assert json_resp['message']['to'][0] == 'invalid phone number, must be of format +441234123123'
|
||||||
|
assert not notify_alpha_client.send_sms.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_reject_missing_message(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value='success'
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'to': '+441234123123'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert json_resp['result'] == 'error'
|
||||||
|
assert len(json_resp['message']) == 1
|
||||||
|
assert len(json_resp['message']['message']) == 1
|
||||||
|
assert json_resp['message']['message'][0] == 'required'
|
||||||
|
assert not notify_alpha_client.send_sms.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_reject_too_short_message(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value='success'
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'to': '+441234123123',
|
||||||
|
'message': ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert json_resp['result'] == 'error'
|
||||||
|
assert len(json_resp['message']) == 1
|
||||||
|
assert len(json_resp['message']['message']) == 1
|
||||||
|
assert json_resp['message']['message'][0] == 'Invalid length. [1 - 160]'
|
||||||
|
assert not notify_alpha_client.send_sms.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_reject_too_long_message(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value='success'
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'to': '+441234123123',
|
||||||
|
'message': '1' * 161
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert json_resp['result'] == 'error'
|
||||||
|
assert len(json_resp['message']) == 1
|
||||||
|
assert len(json_resp['message']['message']) == 1
|
||||||
|
assert json_resp['message']['message'][0] == 'Invalid length. [1 - 160]'
|
||||||
|
assert not notify_alpha_client.send_sms.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_allow_valid_message(
|
||||||
|
notify_api, notify_db, notify_db_session, sample_service, sample_admin_service_id, mocker):
|
||||||
|
"""
|
||||||
|
Tests GET endpoint '/' to retrieve entire service list.
|
||||||
|
"""
|
||||||
|
with notify_api.test_request_context():
|
||||||
|
with notify_api.test_client() as client:
|
||||||
|
mocker.patch(
|
||||||
|
'app.notify_alpha_client.send_sms',
|
||||||
|
return_value={
|
||||||
|
"notification": {
|
||||||
|
"createdAt": "2015-11-03T09:37:27.414363Z",
|
||||||
|
"id": 100,
|
||||||
|
"jobId": 65,
|
||||||
|
"message": "This is the message",
|
||||||
|
"method": "sms",
|
||||||
|
"status": "created",
|
||||||
|
"to": "+449999999999"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
data = {
|
||||||
|
'notification': {
|
||||||
|
'to': '+441234123123',
|
||||||
|
'message': 'valid'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_header = create_authorization_header(
|
||||||
|
service_id=sample_admin_service_id,
|
||||||
|
request_body=json.dumps(data),
|
||||||
|
path=url_for('notifications.create_sms_notification'),
|
||||||
|
method='POST')
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
url_for('notifications.create_sms_notification'),
|
||||||
|
data=json.dumps(data),
|
||||||
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
|
|
||||||
|
json_resp = json.loads(response.get_data(as_text=True))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert json_resp['notification']['id'] == 100
|
||||||
|
notify_alpha_client.send_sms.assert_called_with(mobile_number='+441234123123', message='valid')
|
||||||
@@ -351,10 +351,10 @@ def test_delete_user_not_exists(notify_api, notify_db, notify_db_session, sample
|
|||||||
with notify_api.test_client() as client:
|
with notify_api.test_client() as client:
|
||||||
assert User.query.count() == 2
|
assert User.query.count() == 2
|
||||||
auth_header = create_authorization_header(service_id=sample_admin_service_id,
|
auth_header = create_authorization_header(service_id=sample_admin_service_id,
|
||||||
path=url_for('user.update_user', user_id='123'),
|
path=url_for('user.update_user', user_id='99999'),
|
||||||
method='DELETE')
|
method='DELETE')
|
||||||
resp = client.delete(
|
resp = client.delete(
|
||||||
url_for('user.update_user', user_id="123"),
|
url_for('user.update_user', user_id="99999"),
|
||||||
headers=[('Content-Type', 'application/json'), auth_header])
|
headers=[('Content-Type', 'application/json'), auth_header])
|
||||||
assert resp.status_code == 404
|
assert resp.status_code == 404
|
||||||
assert User.query.count() == 2
|
assert User.query.count() == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user