mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-23 08:51:30 -05:00
Merge branch 'master' into do-not-write-test-data-to-the-history-table
Conflicts: app/dao/notifications_dao.py
This commit is contained in:
@@ -43,15 +43,13 @@ def deliver_sms(self, notification_id):
|
|||||||
send_to_providers.send_sms_to_provider(notification)
|
send_to_providers.send_sms_to_provider(notification)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
current_app.logger.error(
|
current_app.logger.exception(
|
||||||
"RETRY: SMS notification {} failed".format(notification_id)
|
"RETRY: SMS notification {} failed".format(notification_id)
|
||||||
)
|
)
|
||||||
current_app.logger.exception(e)
|
|
||||||
self.retry(queue="retry", countdown=retry_iteration_to_delay(self.request.retries))
|
self.retry(queue="retry", countdown=retry_iteration_to_delay(self.request.retries))
|
||||||
except self.MaxRetriesExceededError:
|
except self.MaxRetriesExceededError:
|
||||||
current_app.logger.error(
|
current_app.logger.exception(
|
||||||
"RETRY FAILED: task send_sms_to_provider failed for notification {}".format(notification_id),
|
"RETRY FAILED: task send_sms_to_provider failed for notification {}".format(notification_id),
|
||||||
e
|
|
||||||
)
|
)
|
||||||
update_notification_status_by_id(notification_id, 'technical-failure')
|
update_notification_status_by_id(notification_id, 'technical-failure')
|
||||||
|
|
||||||
@@ -69,14 +67,12 @@ def deliver_email(self, notification_id):
|
|||||||
update_notification_status_by_id(notification_id, 'technical-failure')
|
update_notification_status_by_id(notification_id, 'technical-failure')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
current_app.logger.error(
|
current_app.logger.exception(
|
||||||
"RETRY: Email notification {} failed".format(notification_id)
|
"RETRY: Email notification {} failed".format(notification_id)
|
||||||
)
|
)
|
||||||
current_app.logger.exception(e)
|
|
||||||
self.retry(queue="retry", countdown=retry_iteration_to_delay(self.request.retries))
|
self.retry(queue="retry", countdown=retry_iteration_to_delay(self.request.retries))
|
||||||
except self.MaxRetriesExceededError:
|
except self.MaxRetriesExceededError:
|
||||||
current_app.logger.error(
|
current_app.logger.error(
|
||||||
"RETRY FAILED: task send_email_to_provider failed for notification {}".format(notification_id),
|
"RETRY FAILED: task send_email_to_provider failed for notification {}".format(notification_id)
|
||||||
e
|
|
||||||
)
|
)
|
||||||
update_notification_status_by_id(notification_id, 'technical-failure')
|
update_notification_status_by_id(notification_id, 'technical-failure')
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ def dao_create_notification(notification):
|
|||||||
|
|
||||||
db.session.add(notification)
|
db.session.add(notification)
|
||||||
if _should_record_notification_in_history_table(notification):
|
if _should_record_notification_in_history_table(notification):
|
||||||
db.session.add(NotificationHistory.from_notification(notification))
|
db.session.add(NotificationHistory.from_original(notification))
|
||||||
|
|
||||||
|
|
||||||
def _should_record_notification_in_history_table(notification):
|
def _should_record_notification_in_history_table(notification):
|
||||||
@@ -200,7 +200,7 @@ def dao_update_notification(notification):
|
|||||||
db.session.add(notification)
|
db.session.add(notification)
|
||||||
if _should_record_notification_in_history_table(notification):
|
if _should_record_notification_in_history_table(notification):
|
||||||
notification_history = NotificationHistory.query.get(notification.id)
|
notification_history = NotificationHistory.query.get(notification.id)
|
||||||
notification_history.update_from_notification(notification)
|
notification_history.update_from_original(notification)
|
||||||
db.session.add(notification_history)
|
db.session.add(notification_history)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy import asc
|
from sqlalchemy import asc
|
||||||
from app.dao.dao_utils import transactional
|
from app.dao.dao_utils import transactional
|
||||||
from app.models import ProviderDetails
|
from app.models import ProviderDetails, ProviderDetailsHistory
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
@@ -20,4 +22,8 @@ def get_provider_details_by_notification_type(notification_type):
|
|||||||
|
|
||||||
@transactional
|
@transactional
|
||||||
def dao_update_provider_details(provider_details):
|
def dao_update_provider_details(provider_details):
|
||||||
|
provider_details.version += 1
|
||||||
|
provider_details.updated_at = datetime.utcnow()
|
||||||
|
history = ProviderDetailsHistory.from_original(provider_details)
|
||||||
db.session.add(provider_details)
|
db.session.add(provider_details)
|
||||||
|
db.session.add(history)
|
||||||
|
|||||||
@@ -35,6 +35,18 @@ def filter_null_value_fields(obj):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryModel:
|
||||||
|
@classmethod
|
||||||
|
def from_original(cls, original):
|
||||||
|
history = cls()
|
||||||
|
history.update_from_original(original)
|
||||||
|
return history
|
||||||
|
|
||||||
|
def update_from_original(self, original):
|
||||||
|
for c in self.__table__.columns:
|
||||||
|
setattr(self, c.name, getattr(original, c.name))
|
||||||
|
|
||||||
|
|
||||||
class User(db.Model):
|
class User(db.Model):
|
||||||
__tablename__ = 'users'
|
__tablename__ = 'users'
|
||||||
|
|
||||||
@@ -354,7 +366,22 @@ class ProviderDetails(db.Model):
|
|||||||
identifier = db.Column(db.String, nullable=False)
|
identifier = db.Column(db.String, nullable=False)
|
||||||
priority = db.Column(db.Integer, nullable=False)
|
priority = db.Column(db.Integer, nullable=False)
|
||||||
notification_type = db.Column(notification_types, nullable=False)
|
notification_type = db.Column(notification_types, nullable=False)
|
||||||
active = db.Column(db.Boolean, default=False)
|
active = db.Column(db.Boolean, default=False, nullable=False)
|
||||||
|
version = db.Column(db.Integer, default=1, nullable=False)
|
||||||
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
|
class ProviderDetailsHistory(db.Model, HistoryModel):
|
||||||
|
__tablename__ = 'provider_details_history'
|
||||||
|
|
||||||
|
id = db.Column(UUID(as_uuid=True), primary_key=True, nullable=False)
|
||||||
|
display_name = db.Column(db.String, nullable=False)
|
||||||
|
identifier = db.Column(db.String, nullable=False)
|
||||||
|
priority = db.Column(db.Integer, nullable=False)
|
||||||
|
notification_type = db.Column(notification_types, nullable=False)
|
||||||
|
active = db.Column(db.Boolean, nullable=False)
|
||||||
|
version = db.Column(db.Integer, primary_key=True, nullable=False)
|
||||||
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
||||||
|
|
||||||
|
|
||||||
JOB_STATUS_PENDING = 'pending'
|
JOB_STATUS_PENDING = 'pending'
|
||||||
@@ -654,7 +681,7 @@ class Notification(db.Model):
|
|||||||
return serialized
|
return serialized
|
||||||
|
|
||||||
|
|
||||||
class NotificationHistory(db.Model):
|
class NotificationHistory(db.Model, HistoryModel):
|
||||||
__tablename__ = 'notification_history'
|
__tablename__ = 'notification_history'
|
||||||
|
|
||||||
id = db.Column(UUID(as_uuid=True), primary_key=True)
|
id = db.Column(UUID(as_uuid=True), primary_key=True)
|
||||||
@@ -680,14 +707,10 @@ class NotificationHistory(db.Model):
|
|||||||
client_reference = db.Column(db.String, nullable=True)
|
client_reference = db.Column(db.String, nullable=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_notification(cls, notification):
|
def from_original(cls, notification):
|
||||||
history = cls(**{c.name: getattr(notification, c.name) for c in cls.__table__.columns})
|
history = super().from_original(notification)
|
||||||
return history
|
return history
|
||||||
|
|
||||||
def update_from_notification(self, notification):
|
|
||||||
for c in self.__table__.columns:
|
|
||||||
setattr(self, c.name, getattr(notification, c.name))
|
|
||||||
|
|
||||||
|
|
||||||
INVITED_USER_STATUS_TYPES = ['pending', 'accepted', 'cancelled']
|
INVITED_USER_STATUS_TYPES = ['pending', 'accepted', 'cancelled']
|
||||||
|
|
||||||
|
|||||||
@@ -37,9 +37,10 @@ def update_provider_details(provider_details_id):
|
|||||||
current_data.update(request.get_json())
|
current_data.update(request.get_json())
|
||||||
update_dict = provider_details_schema.load(current_data).data
|
update_dict = provider_details_schema.load(current_data).data
|
||||||
|
|
||||||
if "identifier" in request.get_json().keys():
|
invalid_keys = {'identifier', 'version', 'updated_at'} & set(key for key in request.get_json().keys())
|
||||||
|
if invalid_keys:
|
||||||
message = "Not permitted to be updated"
|
message = "Not permitted to be updated"
|
||||||
errors = {'identifier': [message]}
|
errors = {key: [message] for key in invalid_keys}
|
||||||
raise InvalidRequest(errors, status_code=400)
|
raise InvalidRequest(errors, status_code=400)
|
||||||
|
|
||||||
dao_update_provider_details(update_dict)
|
dao_update_provider_details(update_dict)
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class Config(object):
|
|||||||
|
|
||||||
REDIS_ENABLED = False
|
REDIS_ENABLED = False
|
||||||
|
|
||||||
SENDING_NOTIFICATIONS_TIMEOUT_PERIOD = 259200
|
SENDING_NOTIFICATIONS_TIMEOUT_PERIOD = 259200 # 3 days
|
||||||
|
|
||||||
SIMULATED_EMAIL_ADDRESSES = ('simulate-delivered@notifications.service.gov.uk',
|
SIMULATED_EMAIL_ADDRESSES = ('simulate-delivered@notifications.service.gov.uk',
|
||||||
'simulate-permanent-failure@notifications.service.gov.uk',
|
'simulate-permanent-failure@notifications.service.gov.uk',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ ENV PYTHONUNBUFFERED=1 \
|
|||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
echo "Install base packages" \
|
echo "Install base packages" \
|
||||||
&& ([ -z "$HTTP_PROXY" ] || echo "Acquire::http::Proxy \"${HTTP_PROXY}\";\n" > /etc/apt/apt.conf.d/99HttpProxy) \
|
&& ([ -z "$HTTP_PROXY" ] || echo "Acquire::http::Proxy \"${HTTP_PROXY}\";" > /etc/apt/apt.conf.d/99HttpProxy) \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& apt-get install -y --no-install-recommends \
|
||||||
make \
|
make \
|
||||||
|
|||||||
54
migrations/versions/0062_provider_details_history.py
Normal file
54
migrations/versions/0062_provider_details_history.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
* add version and updated_at to provider_details
|
||||||
|
* set active to not nullable (any existing null is set to false)
|
||||||
|
* create provider_details_history table, mimicking provider_details
|
||||||
|
|
||||||
|
Revision ID: 0062_provider_details_history
|
||||||
|
Revises: 0061_add_client_reference
|
||||||
|
Create Date: 2016-12-14 13:00:24.226990
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '0062_provider_details_history'
|
||||||
|
down_revision = '0061_add_client_reference'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.get_bind()
|
||||||
|
op.add_column('provider_details', sa.Column('updated_at', sa.DateTime()))
|
||||||
|
|
||||||
|
op.execute('UPDATE provider_details SET active = false WHERE active is null')
|
||||||
|
op.alter_column('provider_details', 'active', nullable=False)
|
||||||
|
|
||||||
|
op.add_column('provider_details', sa.Column('version', sa.Integer(), nullable=True))
|
||||||
|
op.execute('UPDATE provider_details SET version = 1')
|
||||||
|
op.alter_column('provider_details', 'version', nullable=False)
|
||||||
|
|
||||||
|
op.create_table('provider_details_history',
|
||||||
|
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||||
|
sa.Column('display_name', sa.String(), nullable=False),
|
||||||
|
sa.Column('identifier', sa.String(), nullable=False),
|
||||||
|
sa.Column('priority', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('notification_type', postgresql.ENUM('email', 'sms', 'letter', name='notification_type', create_type=False), nullable=False),
|
||||||
|
sa.Column('active', sa.Boolean(), nullable=False),
|
||||||
|
sa.Column('version', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id', 'version')
|
||||||
|
)
|
||||||
|
op.execute(
|
||||||
|
'INSERT INTO provider_details_history' +
|
||||||
|
' (id, display_name, identifier, priority, notification_type, active, version)' +
|
||||||
|
' SELECT id, display_name, identifier, priority, notification_type, active, version FROM provider_details'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table('provider_details_history')
|
||||||
|
|
||||||
|
op.alter_column('provider_details', 'active', existing_type=sa.BOOLEAN(), nullable=True)
|
||||||
|
op.drop_column('provider_details', 'version')
|
||||||
|
op.drop_column('provider_details', 'updated_at')
|
||||||
@@ -98,7 +98,7 @@ def test_send_sms_raises_if_firetext_rejects(mocker, mock_firetext_client):
|
|||||||
assert '"code": 1' in exc.value.text
|
assert '"code": 1' in exc.value.text
|
||||||
|
|
||||||
|
|
||||||
def test_send_sms_raises_if_firetext_rejects(mocker, mock_firetext_client):
|
def test_send_sms_raises_if_firetext_rejects_with_unexpected_data(mocker, mock_firetext_client):
|
||||||
to = content = reference = 'foo'
|
to = content = reference = 'foo'
|
||||||
response_dict = {"something": "gone bad"}
|
response_dict = {"something": "gone bad"}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from datetime import (datetime, date, timedelta)
|
from datetime import (datetime, date, timedelta)
|
||||||
|
|
||||||
|
from sqlalchemy.orm.session import make_transient
|
||||||
import requests_mock
|
import requests_mock
|
||||||
import pytest
|
import pytest
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
@@ -18,6 +19,8 @@ from app.models import (
|
|||||||
Permission,
|
Permission,
|
||||||
ProviderStatistics,
|
ProviderStatistics,
|
||||||
ProviderDetails,
|
ProviderDetails,
|
||||||
|
ProviderDetailsHistory,
|
||||||
|
ProviderRates,
|
||||||
NotificationStatistics,
|
NotificationStatistics,
|
||||||
ServiceWhitelist,
|
ServiceWhitelist,
|
||||||
KEY_TYPE_NORMAL, KEY_TYPE_TEST, KEY_TYPE_TEAM,
|
KEY_TYPE_NORMAL, KEY_TYPE_TEST, KEY_TYPE_TEAM,
|
||||||
@@ -857,3 +860,33 @@ def sample_provider_rate(notify_db, notify_db_session, valid_from=None, rate=Non
|
|||||||
valid_from=valid_from if valid_from is not None else datetime.utcnow(),
|
valid_from=valid_from if valid_from is not None else datetime.utcnow(),
|
||||||
rate=rate if rate is not None else 1,
|
rate=rate if rate is not None else 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def restore_provider_details(notify_db, notify_db_session):
|
||||||
|
"""
|
||||||
|
We view ProviderDetails as a static in notify_db_session, since we don't modify it... except we do, we updated
|
||||||
|
priority. This fixture is designed to be used in tests that will knowingly touch provider details, to restore them
|
||||||
|
to previous state.
|
||||||
|
|
||||||
|
Note: This doesn't technically require notify_db_session (only notify_db), but kept as a requirement to encourage
|
||||||
|
good usage - if you're modifying ProviderDetails' state then it's good to clear down the rest of the DB too
|
||||||
|
"""
|
||||||
|
existing_provider_details = ProviderDetails.query.all()
|
||||||
|
existing_provider_details_history = ProviderDetailsHistory.query.all()
|
||||||
|
# make transient removes the objects from the session - since we'll want to delete them later
|
||||||
|
for epd in existing_provider_details:
|
||||||
|
make_transient(epd)
|
||||||
|
for epdh in existing_provider_details_history:
|
||||||
|
make_transient(epdh)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
# also delete these as they depend on provider_details
|
||||||
|
ProviderRates.query.delete()
|
||||||
|
ProviderDetails.query.delete()
|
||||||
|
ProviderDetailsHistory.query.delete()
|
||||||
|
notify_db.session.commit()
|
||||||
|
notify_db.session.add_all(existing_provider_details)
|
||||||
|
notify_db.session.add_all(existing_provider_details_history)
|
||||||
|
notify_db.session.commit()
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
from app.models import ProviderDetails
|
from datetime import datetime
|
||||||
|
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
from app.models import ProviderDetails, ProviderDetailsHistory
|
||||||
from app import clients
|
from app import clients
|
||||||
from app.dao.provider_details_dao import (
|
from app.dao.provider_details_dao import (
|
||||||
get_provider_details,
|
get_provider_details,
|
||||||
get_provider_details_by_notification_type
|
get_provider_details_by_notification_type,
|
||||||
|
dao_update_provider_details
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_all_providers(notify_db, notify_db_session):
|
def test_can_get_all_providers(restore_provider_details):
|
||||||
assert len(get_provider_details()) == 4
|
assert len(get_provider_details()) == 4
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_sms_providers(notify_db, notify_db_session):
|
def test_can_get_sms_providers(restore_provider_details):
|
||||||
assert len(get_provider_details_by_notification_type('sms')) == 3
|
sms_providers = get_provider_details_by_notification_type('sms')
|
||||||
types = [provider.notification_type for provider in get_provider_details_by_notification_type('sms')]
|
assert len(sms_providers) == 3
|
||||||
assert all('sms' == notification_type for notification_type in types)
|
assert all('sms' == prov.notification_type for prov in sms_providers)
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_sms_providers_in_order(notify_db, notify_db_session):
|
def test_can_get_sms_providers_in_order(restore_provider_details):
|
||||||
providers = get_provider_details_by_notification_type('sms')
|
providers = get_provider_details_by_notification_type('sms')
|
||||||
|
|
||||||
assert providers[0].identifier == "mmg"
|
assert providers[0].identifier == "mmg"
|
||||||
@@ -24,35 +29,52 @@ def test_can_get_sms_providers_in_order(notify_db, notify_db_session):
|
|||||||
assert providers[2].identifier == "loadtesting"
|
assert providers[2].identifier == "loadtesting"
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_email_providers_in_order(notify_db, notify_db_session):
|
def test_can_get_email_providers_in_order(restore_provider_details):
|
||||||
providers = get_provider_details_by_notification_type('email')
|
providers = get_provider_details_by_notification_type('email')
|
||||||
|
|
||||||
assert providers[0].identifier == "ses"
|
assert providers[0].identifier == "ses"
|
||||||
|
|
||||||
|
|
||||||
def test_can_get_email_providers(notify_db, notify_db_session):
|
def test_can_get_email_providers(restore_provider_details):
|
||||||
assert len(get_provider_details_by_notification_type('email')) == 1
|
assert len(get_provider_details_by_notification_type('email')) == 1
|
||||||
types = [provider.notification_type for provider in get_provider_details_by_notification_type('email')]
|
types = [provider.notification_type for provider in get_provider_details_by_notification_type('email')]
|
||||||
assert all('email' == notification_type for notification_type in types)
|
assert all('email' == notification_type for notification_type in types)
|
||||||
|
|
||||||
|
|
||||||
def test_should_error_if_any_provider_in_database_not_in_code(notify_db, notify_db_session, notify_api):
|
def test_should_not_error_if_any_provider_in_code_not_in_database(restore_provider_details):
|
||||||
providers = ProviderDetails.query.all()
|
|
||||||
|
|
||||||
for provider in providers:
|
|
||||||
if provider.notification_type == 'sms':
|
|
||||||
assert clients.get_sms_client(provider.identifier)
|
|
||||||
if provider.notification_type == 'email':
|
|
||||||
assert clients.get_email_client(provider.identifier)
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_error_if_any_provider_in_code_not_in_database(notify_db, notify_db_session, notify_api):
|
|
||||||
providers = ProviderDetails.query.all()
|
providers = ProviderDetails.query.all()
|
||||||
|
|
||||||
ProviderDetails.query.filter_by(identifier='mmg').delete()
|
ProviderDetails.query.filter_by(identifier='mmg').delete()
|
||||||
|
|
||||||
for provider in providers:
|
assert clients.get_sms_client('mmg')
|
||||||
if provider.notification_type == 'sms':
|
|
||||||
assert clients.get_sms_client(provider.identifier)
|
|
||||||
if provider.notification_type == 'email':
|
@freeze_time('2000-01-01T00:00:00')
|
||||||
assert clients.get_email_client(provider.identifier)
|
def test_update_adds_history(restore_provider_details):
|
||||||
|
ses = ProviderDetails.query.filter(ProviderDetails.identifier == 'ses').one()
|
||||||
|
ses_history = ProviderDetailsHistory.query.filter(ProviderDetailsHistory.id == ses.id).one()
|
||||||
|
|
||||||
|
assert ses.version == 1
|
||||||
|
assert ses_history.version == 1
|
||||||
|
assert ses.updated_at is None
|
||||||
|
|
||||||
|
ses.active = False
|
||||||
|
|
||||||
|
dao_update_provider_details(ses)
|
||||||
|
|
||||||
|
assert not ses.active
|
||||||
|
assert ses.updated_at == datetime(2000, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
|
ses_history = ProviderDetailsHistory.query.filter(
|
||||||
|
ProviderDetailsHistory.id == ses.id
|
||||||
|
).order_by(
|
||||||
|
ProviderDetailsHistory.version
|
||||||
|
).all()
|
||||||
|
|
||||||
|
assert ses_history[0].active
|
||||||
|
assert ses_history[0].version == 1
|
||||||
|
assert ses_history[0].updated_at is None
|
||||||
|
|
||||||
|
assert not ses_history[1].active
|
||||||
|
assert ses_history[1].version == 2
|
||||||
|
assert ses_history[1].updated_at == datetime(2000, 1, 1, 0, 0, 0)
|
||||||
|
|||||||
@@ -1,150 +1,103 @@
|
|||||||
|
import pytest
|
||||||
from flask import json
|
from flask import json
|
||||||
|
|
||||||
|
from app.models import ProviderDetails
|
||||||
|
|
||||||
from tests import create_authorization_header
|
from tests import create_authorization_header
|
||||||
|
|
||||||
|
|
||||||
def test_get_provider_details_in_type_and_identifier_order(notify_db, notify_db_session, notify_api):
|
def test_get_provider_details_in_type_and_identifier_order(client, notify_db):
|
||||||
with notify_api.test_request_context():
|
response = client.get(
|
||||||
with notify_api.test_client() as client:
|
'/provider-details',
|
||||||
auth_header = create_authorization_header()
|
headers=[create_authorization_header()]
|
||||||
response = client.get(
|
)
|
||||||
'/provider-details',
|
assert response.status_code == 200
|
||||||
headers=[auth_header]
|
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
||||||
)
|
assert len(json_resp) == 4
|
||||||
assert response.status_code == 200
|
|
||||||
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
|
||||||
assert len(json_resp) == 4
|
|
||||||
|
|
||||||
assert json_resp[0]['identifier'] == 'ses'
|
assert json_resp[0]['identifier'] == 'ses'
|
||||||
assert json_resp[1]['identifier'] == 'mmg'
|
assert json_resp[1]['identifier'] == 'mmg'
|
||||||
assert json_resp[2]['identifier'] == 'firetext'
|
assert json_resp[2]['identifier'] == 'firetext'
|
||||||
assert json_resp[3]['identifier'] == 'loadtesting'
|
assert json_resp[3]['identifier'] == 'loadtesting'
|
||||||
|
|
||||||
|
|
||||||
def test_get_provider_details_by_id(notify_db, notify_db_session, notify_api):
|
def test_get_provider_details_by_id(client, notify_db):
|
||||||
with notify_api.test_request_context():
|
response = client.get(
|
||||||
with notify_api.test_client() as client:
|
'/provider-details',
|
||||||
auth_header = create_authorization_header()
|
headers=[create_authorization_header()]
|
||||||
response = client.get(
|
)
|
||||||
'/provider-details',
|
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
||||||
headers=[auth_header]
|
|
||||||
)
|
|
||||||
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
|
||||||
|
|
||||||
provider_resp = client.get(
|
provider_resp = client.get(
|
||||||
'/provider-details/{}'.format(json_resp[0]['id']),
|
'/provider-details/{}'.format(json_resp[0]['id']),
|
||||||
headers=[auth_header]
|
headers=[create_authorization_header()]
|
||||||
)
|
)
|
||||||
|
|
||||||
provider = json.loads(provider_resp.get_data(as_text=True))['provider_details']
|
provider = json.loads(provider_resp.get_data(as_text=True))['provider_details']
|
||||||
assert provider['identifier'] == json_resp[0]['identifier']
|
assert provider['identifier'] == json_resp[0]['identifier']
|
||||||
|
|
||||||
|
|
||||||
def test_get_provider_details_contains_correct_fields(notify_db, notify_db_session, notify_api):
|
def test_get_provider_details_contains_correct_fields(client, notify_db):
|
||||||
with notify_api.test_request_context():
|
response = client.get(
|
||||||
with notify_api.test_client() as client:
|
'/provider-details',
|
||||||
auth_header = create_authorization_header()
|
headers=[create_authorization_header()]
|
||||||
response = client.get(
|
)
|
||||||
'/provider-details',
|
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
||||||
headers=[auth_header]
|
allowed_keys = {
|
||||||
)
|
"id", "display_name", "identifier", "priority", 'notification_type', "active", "version", "updated_at"
|
||||||
json_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
}
|
||||||
allowed_keys = {"id", "display_name", "identifier", "priority", 'notification_type', "active"}
|
assert allowed_keys == set(json_resp[0].keys())
|
||||||
assert \
|
|
||||||
allowed_keys == \
|
|
||||||
set(json_resp[0].keys())
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_be_able_to_update_priority(notify_db, notify_db_session, notify_api):
|
def test_should_be_able_to_update_priority(client, restore_provider_details):
|
||||||
with notify_api.test_request_context():
|
provider = ProviderDetails.query.first()
|
||||||
with notify_api.test_client() as client:
|
|
||||||
auth_header = create_authorization_header()
|
|
||||||
response = client.get(
|
|
||||||
'/provider-details',
|
|
||||||
headers=[auth_header]
|
|
||||||
)
|
|
||||||
fetch_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
|
||||||
|
|
||||||
provider_id = fetch_resp[2]['id']
|
update_resp = client.post(
|
||||||
|
'/provider-details/{}'.format(provider.id),
|
||||||
update_resp = client.post(
|
headers=[('Content-Type', 'application/json'), create_authorization_header()],
|
||||||
'/provider-details/{}'.format(provider_id),
|
data=json.dumps({
|
||||||
headers=[('Content-Type', 'application/json'), auth_header],
|
'priority': 5
|
||||||
data=json.dumps({
|
})
|
||||||
'priority': 5
|
)
|
||||||
})
|
assert update_resp.status_code == 200
|
||||||
)
|
update_json = json.loads(update_resp.get_data(as_text=True))['provider_details']
|
||||||
assert update_resp.status_code == 200
|
assert update_json['identifier'] == provider.identifier
|
||||||
update_json = json.loads(update_resp.get_data(as_text=True))['provider_details']
|
assert update_json['priority'] == 5
|
||||||
assert update_json['identifier'] == 'firetext'
|
assert provider.priority == 5
|
||||||
assert update_json['priority'] == 5
|
|
||||||
|
|
||||||
update_resp = client.post(
|
|
||||||
'/provider-details/{}'.format(provider_id),
|
|
||||||
headers=[('Content-Type', 'application/json'), auth_header],
|
|
||||||
data=json.dumps({
|
|
||||||
'priority': 20
|
|
||||||
})
|
|
||||||
)
|
|
||||||
assert update_resp.status_code == 200
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_be_able_to_update_status(notify_db, notify_db_session, notify_api):
|
def test_should_be_able_to_update_status(client, restore_provider_details):
|
||||||
with notify_api.test_request_context():
|
provider = ProviderDetails.query.first()
|
||||||
with notify_api.test_client() as client:
|
|
||||||
auth_header = create_authorization_header()
|
|
||||||
response = client.get(
|
|
||||||
'/provider-details',
|
|
||||||
headers=[auth_header]
|
|
||||||
)
|
|
||||||
fetch_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
|
||||||
|
|
||||||
firetext = next(x for x in fetch_resp if x['identifier'] == 'firetext')
|
update_resp_1 = client.post(
|
||||||
|
'/provider-details/{}'.format(provider.id),
|
||||||
update_resp_1 = client.post(
|
headers=[('Content-Type', 'application/json'), create_authorization_header()],
|
||||||
'/provider-details/{}'.format(firetext['id']),
|
data=json.dumps({
|
||||||
headers=[('Content-Type', 'application/json'), auth_header],
|
'active': False
|
||||||
data=json.dumps({
|
})
|
||||||
'active': False
|
)
|
||||||
})
|
assert update_resp_1.status_code == 200
|
||||||
)
|
update_resp_1 = json.loads(update_resp_1.get_data(as_text=True))['provider_details']
|
||||||
assert update_resp_1.status_code == 200
|
assert update_resp_1['identifier'] == provider.identifier
|
||||||
update_resp_1 = json.loads(update_resp_1.get_data(as_text=True))['provider_details']
|
assert not update_resp_1['active']
|
||||||
assert update_resp_1['identifier'] == 'firetext'
|
assert not provider.active
|
||||||
assert not update_resp_1['active']
|
|
||||||
|
|
||||||
update_resp_2 = client.post(
|
|
||||||
'/provider-details/{}'.format(firetext['id']),
|
|
||||||
headers=[('Content-Type', 'application/json'), auth_header],
|
|
||||||
data=json.dumps({
|
|
||||||
'active': True
|
|
||||||
})
|
|
||||||
)
|
|
||||||
assert update_resp_2.status_code == 200
|
|
||||||
update_resp_2 = json.loads(update_resp_2.get_data(as_text=True))['provider_details']
|
|
||||||
assert update_resp_2['identifier'] == 'firetext'
|
|
||||||
assert update_resp_2['active']
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_be_able_to_update_identifier(notify_db, notify_db_session, notify_api):
|
@pytest.mark.parametrize('field,value', [
|
||||||
with notify_api.test_request_context():
|
('identifier', 'new'),
|
||||||
with notify_api.test_client() as client:
|
('version', 7),
|
||||||
auth_header = create_authorization_header()
|
('updated_at', None)
|
||||||
response = client.get(
|
])
|
||||||
'/provider-details',
|
def test_should_not_be_able_to_update_disallowed_fields(client, restore_provider_details, field, value):
|
||||||
headers=[auth_header]
|
provider = ProviderDetails.query.first()
|
||||||
)
|
|
||||||
fetch_resp = json.loads(response.get_data(as_text=True))['provider_details']
|
|
||||||
|
|
||||||
provider_id = fetch_resp[2]['id']
|
update_resp = client.post(
|
||||||
|
'/provider-details/{}'.format(provider.id),
|
||||||
update_resp = client.post(
|
headers=[('Content-Type', 'application/json'), create_authorization_header()],
|
||||||
'/provider-details/{}'.format(provider_id),
|
data=json.dumps({field: value})
|
||||||
headers=[('Content-Type', 'application/json'), auth_header],
|
)
|
||||||
data=json.dumps({
|
assert update_resp.status_code == 400
|
||||||
'identifier': "new"
|
update_resp = json.loads(update_resp.get_data(as_text=True))
|
||||||
})
|
print(update_resp)
|
||||||
)
|
assert update_resp['message'][field][0] == 'Not permitted to be updated'
|
||||||
assert update_resp.status_code == 400
|
assert update_resp['result'] == 'error'
|
||||||
update_resp = json.loads(update_resp.get_data(as_text=True))
|
|
||||||
assert update_resp['message']['identifier'][0] == 'Not permitted to be updated'
|
|
||||||
assert update_resp['result'] == 'error'
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ def notify_db_session(notify_db):
|
|||||||
|
|
||||||
notify_db.session.remove()
|
notify_db.session.remove()
|
||||||
for tbl in reversed(notify_db.metadata.sorted_tables):
|
for tbl in reversed(notify_db.metadata.sorted_tables):
|
||||||
if tbl.name not in ["provider_details", "key_types", "branding_type", "job_status"]:
|
if tbl.name not in ["provider_details", "key_types", "branding_type", "job_status", "provider_details_history"]:
|
||||||
notify_db.engine.execute(tbl.delete())
|
notify_db.engine.execute(tbl.delete())
|
||||||
notify_db.session.commit()
|
notify_db.session.commit()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user