wcs/tests/utilities.py

314 lines
11 KiB
Python

import cPickle
import email.header
import email.parser
import os
import tempfile
import random
import psycopg2
import pytest
import shutil
import threading
import urlparse
from wcs import sql
from webtest import TestApp
from quixote import cleanup, get_publisher
from django.conf import settings
import wcs
import wcs.wsgi
from wcs import publisher, compat
from wcs.qommon.http_request import HTTPRequest
from wcs.users import User
from wcs.tracking_code import TrackingCode
import wcs.qommon.sms
import qommon.sms
from qommon.errors import ConnectionError
class KnownElements(object):
pickle_app_dir = None
sql_app_dir = None
sql_db_name = None
known_elements = KnownElements()
def create_temporary_pub(sql_mode=False):
if sql_mode is True:
if pytest.config.getoption('without_postgresql_tests'):
pytest.skip("unsupported configuration")
return
if get_publisher():
get_publisher().cleanup()
cleanup()
if sql_mode is False and known_elements.pickle_app_dir:
APP_DIR = known_elements.pickle_app_dir
elif sql_mode is True and known_elements.sql_app_dir:
APP_DIR = known_elements.sql_app_dir
else:
APP_DIR = tempfile.mkdtemp()
if sql_mode is True:
known_elements.sql_app_dir = APP_DIR
elif sql_mode is False:
known_elements.pickle_app_dir = APP_DIR
compat.CompatWcsPublisher.APP_DIR = APP_DIR
compat.CompatWcsPublisher.DATA_DIR = os.path.abspath(
os.path.join(os.path.dirname(wcs.__file__), '..', 'data'))
compat.CompatWcsPublisher.cronjobs = None
pub = compat.CompatWcsPublisher.create_publisher()
# allow saving the user
pub.app_dir = os.path.join(APP_DIR, 'example.net')
pub.site_charset = 'utf-8'
if sql_mode:
pub.user_class = sql.SqlUser
pub.tracking_code_class = sql.TrackingCode
pub.is_using_postgresql = lambda: True
else:
pub.user_class = User
pub.tracking_code_class = TrackingCode
pub.is_using_postgresql = lambda: False
if os.path.exists(os.path.join(pub.app_dir, 'wcs.log')):
os.unlink(os.path.join(pub.app_dir, 'wcs.log'))
if os.path.exists(os.path.join(pub.APP_DIR, 'scripts')):
shutil.rmtree(os.path.join(pub.APP_DIR, 'scripts'))
if os.path.exists(os.path.join(pub.app_dir, 'scripts')):
shutil.rmtree(os.path.join(pub.app_dir, 'scripts'))
if os.path.exists(pub.app_dir):
pub.cfg = {}
if sql_mode:
pub.cfg['postgresql'] = {'database': known_elements.sql_db_name, 'user': os.environ['USER']}
pub.cfg['misc'] = {'charset': 'utf-8'}
pub.cfg['language'] = {'language': 'en'}
pub.write_cfg()
return pub
os.mkdir(pub.app_dir)
fd = file(os.path.join(pub.app_dir, 'site-options.cfg'), 'w')
fd.write('[wscall-secrets]\n')
fd.write('idp.example.net = BAR\n')
fd.write('\n')
fd.write('[options]\n')
fd.write('formdef-captcha-option = true\n')
fd.write('workflow-resubmit-action = true\n')
fd.write('workflow-global-actions = true\n')
fd.write('workflow-criticality-levels = true\n')
if sql_mode:
fd.write('postgresql = true\n')
conn = psycopg2.connect(user=os.environ['USER'])
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
cur = conn.cursor()
dbname = 'wcstests%d' % random.randint(0, 100000)
known_elements.sql_db_name = dbname
cur.execute('CREATE DATABASE %s' % dbname)
cur.close()
pub.cfg['postgresql'] = {'database': dbname, 'user': os.environ['USER']}
pub.cfg['misc'] = {'charset': 'utf-8'}
pub.write_cfg()
sql.do_user_table()
sql.do_tracking_code_table()
conn.close()
fd.close()
return pub
def clean_temporary_pub():
if get_publisher():
get_publisher().cleanup()
if known_elements.pickle_app_dir and os.path.exists(known_elements.pickle_app_dir):
shutil.rmtree(known_elements.pickle_app_dir)
known_elements.pickle_app_dir = None
if known_elements.sql_app_dir and os.path.exists(known_elements.sql_app_dir):
shutil.rmtree(known_elements.sql_app_dir)
known_elements.sql_app_dir = None
if known_elements.sql_db_name:
conn = psycopg2.connect(user=os.environ['USER'])
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
try:
cur = conn.cursor()
cur.execute('DROP DATABASE %s' % known_elements.sql_db_name)
cur.close()
except psycopg2.Error:
pass
known_elements.sql_db_name = None
def get_app(pub, https=False):
extra_environ = {'HTTP_HOST': 'example.net', 'REMOTE_ADDR': '127.0.0.1'}
if https:
settings.SECURE_PROXY_SSL_HEADER = ('HTTPS', 'on')
extra_environ['HTTPS'] = 'on'
else:
extra_environ['HTTPS'] = 'off'
return TestApp(wcs.wsgi.application, extra_environ=extra_environ)
def login(app, username='admin', password='admin'):
login_page = app.get('/login/')
login_form = login_page.forms['login-form']
login_form['username'] = username
login_form['password'] = password
resp = login_form.submit()
assert resp.status_int == 302
return app
class EmailsMocking(object):
def create_smtp_server(self, *args, **kwargs):
class MockSmtplibSMTP(object):
def __init__(self, emails):
self.emails = emails
def sendmail(self, msg_from, rcpts, msg):
msg = email.parser.Parser().parsestr(msg)
subject = email.header.decode_header(msg['Subject'])[0][0]
if msg.is_multipart():
payload = msg.get_payload()[0].get_payload(decode=True)
else:
payload = msg.get_payload(decode=True)
self.emails[subject] = {
'from': msg_from,
'to': email.header.decode_header(msg['To'])[0][0],
'payload': payload,
'msg': msg,
}
self.emails[subject]['email_rcpt'] = rcpts
def close(self):
pass
return MockSmtplibSMTP(self.emails)
def get(self, subject):
return self.emails.get(subject)
def empty(self):
self.emails.clear()
def count(self):
return len(self.emails)
def __enter__(self):
import wcs.qommon.emails
import qommon.emails
self.wcs_create_smtp_server = wcs.qommon.emails.create_smtp_server
self.qommon_create_smtp_server = qommon.emails.create_smtp_server
wcs.qommon.emails.create_smtp_server = self.create_smtp_server
qommon.emails.create_smtp_server = self.create_smtp_server
self.emails = {}
return self
def __exit__(self, exc_type, exc_value, tb):
del self.emails
wcs.qommon.emails.create_smtp_server = self.wcs_create_smtp_server
qommon.emails.create_smtp_server = self.qommon_create_smtp_server
class MockSubstitutionVariables(object):
def get_substitution_variables(self):
return {'bar': 'Foobar', 'foo': '1 < 3', 'email': 'sub@localhost',
'empty': ''}
class HttpRequestsMocking(object):
def __init__(self):
self.requests = []
import wcs.qommon.misc
import qommon.misc
wcs.qommon.misc._http_request = self.http_request
qommon.misc._http_request = self.http_request
def http_request(self, url, method='GET', body=None, headers={}, timeout=None):
self.requests.append(
{'url': url,
'method': method,
'body': body,
'headers': headers,
'timeout': timeout})
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
base_url = urlparse.urlunparse((scheme, netloc, path, '', '', ''))
status, data, headers = {
'http://remote.example.net/204': (204, None, None),
'http://remote.example.net/400-json': (400, '{"err": 1, "err_desc": ":("}', None),
'http://remote.example.net/404': (404, 'page not found', None),
'http://remote.example.net/404-json': (404, '{"err": 1}', None),
'http://remote.example.net/500': (500, 'internal server error', None),
'http://remote.example.net/json': (200, '{"foo": "bar"}', None),
'http://remote.example.net/json-err0': (200, '{"data": "foo", "err": 0}', None),
'http://remote.example.net/json-err1': (200, '{"data": "", "err": 1}', None),
'http://remote.example.net/json-errstr': (200, '{"data": "", "err": "bug"}', None),
'http://remote.example.net/json-errheader0': (200, '{"foo": "bar"}',
{'x-error-code': '0'}),
'http://remote.example.net/json-errheader1': (200, '{"foo": "bar"}',
{'x-error-code': '1'}),
'http://remote.example.net/json-errheaderstr': (200, '{"foo": "bar"}',
{'x-error-code': 'bug'}),
'http://remote.example.net/xml': (200, '<?xml version="1.0"><foo/>',
{'content-type': 'text/xml'}),
'http://remote.example.net/xml-errheader': (200, '<?xml version="1.0"><foo/>',
{'content-type': 'text/xml', 'x-error-code': '1'}),
'http://remote.example.net/connection-error': (None, None, None),
}.get(base_url, (200, '', {}))
class FakeResponse(object):
def __init__(self, status, data, headers):
self.status = status
self.reason = 'whatever'
self.data = data
self.headers = headers or {}
self.length = len(data or '')
def getheader(self, header):
return self.headers.get(header, None)
if status is None:
raise ConnectionError('error')
return FakeResponse(status, data, headers), status, data, None
def get_last(self, attribute):
return self.requests[-1][attribute]
def empty(self):
self.requests = []
http_requests = HttpRequestsMocking()
class SMSMocking(wcs.qommon.sms.MobytSMS):
def get_sms_class(self, mode):
if mode == 'none':
return None
return self
def send(self, sender, destinations, text, quality=None):
self.sms.append({'sender': sender, 'destinations': destinations, 'text': text})
def get_sms_left(self, type='standard'):
raise NotImplementedError
def get_money_left(self):
raise NotImplementedError
def __enter__(self):
self.sms = []
self.wcs_get_sms_class = wcs.qommon.sms.SMS.get_sms_class
self.qommon_get_sms_class = qommon.sms.SMS.get_sms_class
wcs.qommon.sms.SMS.get_sms_class = self.get_sms_class
qommon.sms.SMS.get_sms_class = self.get_sms_class
return self
def __exit__(self, exc_type, exc_value, tb):
del self.sms
wcs.qommon.sms.SMS.get_sms_class = self.wcs_get_sms_class
qommon.sms.SMS.get_sms_class = self.qommon_get_sms_class