clarify and test overlimit notifications code.

This commit is contained in:
fiatjaf 2018-08-29 19:34:20 +00:00
parent 0fb8df565c
commit ac77c29ab7
7 changed files with 94 additions and 47 deletions

View File

@ -198,9 +198,10 @@ class Form(DB.Model):
# check if the forms are over the counter and the user is not upgraded
overlimit = False
monthly_counter = self.get_monthly_counter()
monthly_limit = settings.MONTHLY_SUBMISSIONS_LIMIT if \
self.id > settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE \
monthly_limit = settings.MONTHLY_SUBMISSIONS_LIMIT \
if self.id > settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE \
else settings.GRANDFATHER_MONTHLY_LIMIT
if monthly_counter > monthly_limit and not self.upgraded:
overlimit = True
@ -208,7 +209,7 @@ class Form(DB.Model):
# send email notification
send_email(
to=self.email,
subject="[WARNING] Approaching submission limit",
subject="Formspree Notice: Approaching submission limit.",
text=render_template('email/90-percent-warning.txt',
unconfirm_url=unconfirm, limit=monthly_limit
),
@ -222,6 +223,7 @@ class Form(DB.Model):
now = datetime.datetime.utcnow().strftime('%I:%M %p UTC - %d %B %Y')
if not overlimit:
g.log.info('Submitted.')
text = render_template('email/form.txt',
data=data, host=self.host, keys=keys, now=now,
unconfirm_url=unconfirm)
@ -235,19 +237,20 @@ class Form(DB.Model):
data=data, host=self.host, keys=keys, now=now,
unconfirm_url=unconfirm)
else:
if monthly_counter - monthly_limit > 25:
g.log.info('Submission rejected. Form over quota.',
monthly_counter=monthly_counter)
# only send this overlimit notification for the first 25 overlimit emails
# after that, return an error so the user can know the website owner is not
# going to read his message.
g.log.info('Submission rejected. Form over quota.',
monthly_counter=monthly_counter)
# send an overlimit notification for the first x overlimit emails
# after that, return an error so the user can know the website owner is not
# going to read his message.
if monthly_counter <= monthly_limit + settings.OVERLIMIT_NOTIFICATION_QUANTITY:
subject = 'Formspree Notice: Your submission limit has been reached.'
text = render_template('email/overlimit-notification.txt',
host=self.host, unconfirm_url=unconfirm, limit=monthly_limit)
html = render_template_string(TEMPLATES.get('overlimit-notification.html'),
host=self.host, unconfirm_url=unconfirm, limit=monthly_limit)
else:
return {'code': Form.STATUS_OVERLIMIT}
text = render_template('email/overlimit-notification.txt',
host=self.host, unconfirm_url=unconfirm, limit=monthly_limit)
html = render_template_string(TEMPLATES.get('overlimit-notification.html'),
host=self.host, unconfirm_url=unconfirm, limit=monthly_limit)
# if emails are disabled and form is upgraded, don't send email notification
if self.disable_email and self.upgraded:
return {'code': Form.STATUS_NO_EMAIL, 'next': next}

View File

@ -95,7 +95,6 @@ def send(email_or_string):
g.log = g.log.bind(host=host, wants='json' if request_wants_json() else 'html')
g.log.info('Submitted.')
if not IS_VALID_EMAIL(email_or_string):
# in this case it can be a hashid identifying a
# form generated from the dashboard

View File

@ -19,6 +19,7 @@ HASHIDS_SALT = os.getenv('HASHIDS_SALT') or ''
NONCE_SECRET = (os.getenv('NONCE_SECRET') or '').encode('utf-8')
GRANDFATHER_MONTHLY_LIMIT = 1000
OVERLIMIT_NOTIFICATION_QUANTITY = 25
MONTHLY_SUBMISSIONS_LIMIT = int(os.getenv('MONTHLY_SUBMISSIONS_LIMIT') or 100)
ARCHIVED_SUBMISSIONS_LIMIT = int(os.getenv('ARCHIVED_SUBMISSIONS_LIMIT') or 1000)
FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE = int(os.getenv('FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE') or 0)

View File

@ -33,6 +33,7 @@ def msend():
def app():
settings.MONTHLY_SUBMISSIONS_LIMIT = 2
settings.ARCHIVED_SUBMISSIONS_LIMIT = 2
settings.OVERLIMIT_NOTIFICATION_QUANTITY = 2
settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE = 0
settings.EXPENSIVELY_WIPE_SUBMISSIONS_FREQUENCY = 1
settings.PRESERVE_CONTEXT_ON_EXCEPTION = False

View File

@ -219,50 +219,59 @@ def test_grandfather_limit_and_decrease(client, msend):
settings.GRANDFATHER_MONTHLY_LIMIT = 2
settings.MONTHLY_SUBMISSIONS_LIMIT = 1
settings.FORM_LIMIT_DECREASE_ACTIVATION_SEQUENCE = 1
# the form limit should be 3 for the first form and 1 for the second
# the form limit should be 2 for the first form and 1 for the second
# submit the forms
client.post('/1@example.com',
client.post('/grandfathered@example.com',
headers = {'referer': 'http://somewhere.com'},
data={'name': 'john'}
)
form1 = Form.query.filter_by(host='somewhere.com',
email='1@example.com').first()
form_grandfathered = Form.query.filter_by(host='somewhere.com',
email='grandfathered@example.com').first()
client.post('/2@example.com',
client.post('/new@example.com',
headers = {'referer': 'http://somewhere.com'},
data={'name': 'john'}
)
form2 = Form.query.filter_by(host='somewhere.com',
email='2@example.com').first()
form_new = Form.query.filter_by(host='somewhere.com',
email='new@example.com').first()
# confirm formS
form1.confirmed = True
DB.session.add(form1)
form2.confirmed = True
DB.session.add(form2)
form_grandfathered.confirmed = True
DB.session.add(form_grandfathered)
form_new.confirmed = True
DB.session.add(form_new)
DB.session.commit()
# submit each form 3 times
for _ in range(3):
client.post('/1@example.com',
msend.reset_mock()
for i in range(3):
client.post('/grandfathered@example.com',
headers = {'referer': 'http://somewhere.com'},
data={'_replyto': 'johann@gmail.com', 'name': 'johann', 'value': 'fivela'}
)
client.post('/2@example.com',
headers = {'referer': 'http://somewhere.com'},
data={'_replyto': 'johann@gmail.com', 'name': 'johann', 'value': 'fivela'}
data={'_replyto': 'johann@gmail.com', 'name': 'johann', 'value': 'v%s' % i}
)
assert '1@example.com' == msend.call_args_list[-6][1]['to']
assert 'fivela' in msend.call_args_list[-6][1]['text']
assert '2@example.com' == msend.call_args_list[-5][1]['to']
assert 'fivela' in msend.call_args_list[-5][1]['text']
assert '1@example.com' == msend.call_args_list[-4][1]['to']
assert 'fivela' in msend.call_args_list[-4][1]['text']
assert '2@example.com' == msend.call_args_list[-3][1]['to']
assert 'past the limit of 1' in msend.call_args_list[-3][1]['text']
assert '1@example.com' == msend.call_args_list[-2][1]['to']
assert 'past the limit of 2' in msend.call_args_list[-2][1]['text']
assert '2@example.com' == msend.call_args_list[-1][1]['to']
assert 'past the limit of 1' in msend.call_args_list[-1][1]['text']
assert len(msend.call_args_list) == 4
assert 'grandfathered@example.com' == msend.call_args_list[-4][1]['to']
assert '90%' in msend.call_args_list[-4][1]['text']
assert 'grandfathered@example.com' == msend.call_args_list[-3][1]['to']
assert 'v0' in msend.call_args_list[-3][1]['text']
assert 'grandfathered@example.com' == msend.call_args_list[-2][1]['to']
assert 'v1' in msend.call_args_list[-2][1]['text']
assert 'grandfathered@example.com' == msend.call_args_list[-1][1]['to']
assert 'limit' in msend.call_args_list[-1][1]['text']
msend.reset_mock()
for i in range(3):
client.post('/new@example.com',
headers = {'referer': 'http://somewhere.com'},
data={'_replyto': 'johann@gmail.com', 'name': 'johann', 'value': 'v%s' % i}
)
assert len(msend.call_args_list) == 3
assert 'new@example.com' == msend.call_args_list[-3][1]['to']
assert 'v0' in msend.call_args_list[-3][1]['text']
assert 'new@example.com' == msend.call_args_list[-2][1]['to']
assert 'limit' in msend.call_args_list[-2][1]['text']
assert 'new@example.com' == msend.call_args_list[-1][1]['to']
assert 'limit' in msend.call_args_list[-1][1]['text']

View File

@ -53,7 +53,7 @@ def test_various_content_types(client, msend):
# for this test only we will relax this limit.
default_limit = settings.MONTHLY_SUBMISSIONS_LIMIT
settings.MONTHLY_SUBMISSIONS_LIMIT = len(types)
settings.MONTHLY_SUBMISSIONS_LIMIT = len(types) * 2
for ct, acc, check in types:
headers = {'Referer': 'http://testwebsite.com'}

View File

@ -223,7 +223,7 @@ def test_monthly_limits(client, msend):
assert r.status_code == 302 # the response to the user is the same
# being the form over the limits or not
# but the mocked sendgrid should never receive this last form
# the mocked sendgrid should never receive this last form
assert 'maria' not in msend.call_args[1]['text']
assert 'past the limit' in msend.call_args[1]['text']
@ -252,6 +252,40 @@ def test_monthly_limits(client, msend):
assert r.status_code == 302
assert 'noah' in msend.call_args[1]['text']
def test_overlimit_notifications(client, msend):
# monthly limit is set to 2 during tests
assert settings.MONTHLY_SUBMISSIONS_LIMIT == 2
# we'll send two overlimit notifications and no more
assert settings.OVERLIMIT_NOTIFICATION_QUANTITY == 2
# manually verify luke@example.com
r = client.post('/luke@testwebsite.com',
headers=http_headers,
data={'name': 'luke'}
)
f = Form.query.first()
f.confirm_sent = True
f.confirmed = True
DB.session.add(f)
DB.session.commit()
# submit the form multiple times
msend.reset_mock()
for i in range(0, 20):
r = client.post('/luke@testwebsite.com',
headers=http_headers,
data={'name': 'matthew'}
)
# but we'll only send 5 emails (1 warning, 2 normal, 2 overlimit)
assert len(msend.call_args_list) == 5
assert '90%' in msend.call_args_list[-5][1]['text']
assert 'matthew' in msend.call_args_list[-4][1]['text']
assert 'matthew' in msend.call_args_list[-3][1]['text']
assert 'limit' in msend.call_args_list[-2][1]['text']
assert 'limit' in msend.call_args_list[-1][1]['text']
def test_first_submission_is_stored(client, msend):
r = client.post('/what@firstsubmissed.com',
headers=http_headers,