diff --git a/smoke_test.py b/smoke_test.py new file mode 100644 index 00000000..981d4bac --- /dev/null +++ b/smoke_test.py @@ -0,0 +1,87 @@ +""" +Smoke test for a running stack: opens every site and the admin login. + + docker compose exec web python smoke_test.py [base_url] + +Needs no extra packages, so it runs the same on every Python/Django version. +Exits non-zero if anything fails. Expects the sample data (SEED_DUMMY_DATA). +""" +import json +import os +import re +import sys +import urllib.error +import urllib.parse +import urllib.request +from http.cookiejar import CookieJar + +from utils.sample_sites import SITE_TEXTS + +BASE = sys.argv[1] if len(sys.argv) > 1 else 'http://127.0.0.1:8000' +ADMIN = ('dummy-admin@example.com', + os.environ.get('DUMMY_ADMIN_PASSWORD') or 'dummy-admin') +SUB_PAGES = ['about', 'services', 'pricing', 'contact'] +failures = [] + + +def fetch(opener, path, host, data=None): + request = urllib.request.Request(BASE + path, data=data) + request.add_header('Host', host) + try: + response = opener.open(request, timeout=60) + except urllib.error.HTTPError as error: + return error.code, error.read().decode('utf-8', 'replace'), path + return (response.status if hasattr(response, 'status') + else response.getcode(), + response.read().decode('utf-8', 'replace'), response.geturl()) + + +def check(name, ok, detail=''): + print('{} {}{}'.format('ok ' if ok else 'FAIL', name, + '' if ok else ' -> ' + detail)) + if not ok: + failures.append(name) + + +def sites(): + configs = json.loads(os.environ.get('UNGLEICH_SITE_CONFIGS') or '{}') + return sorted(d for d in configs if ':' not in d) + + +def main(): + for domain in sites(): + opener = urllib.request.build_opener( + urllib.request.HTTPCookieProcessor(CookieJar())) + name, tagline = SITE_TEXTS.get(domain, (domain, '')) + status, body, _ = fetch(opener, '/', domain) + check('{} /'.format(domain), status == 200, 'status %s' % status) + status, body, _ = fetch(opener, '/en-us/cms/', domain) + check('{} home'.format(domain), + status == 200 and name in body and tagline in body, + 'status %s, content missing' % status) + for slug in SUB_PAGES: + status, _, _ = fetch(opener, '/en-us/cms/%s/' % slug, domain) + check('{} {}'.format(domain, slug), status == 200, + 'status %s' % status) + + # admin login on the first site + domain = sites()[0] + opener = urllib.request.build_opener( + urllib.request.HTTPCookieProcessor(CookieJar())) + status, body, _ = fetch(opener, '/en-us/admin/login/', domain) + token = re.search(r"name=.csrfmiddlewaretoken. value=.([^'\"]+)", body) + check('admin login page', status == 200 and token, 'status %s' % status) + if token: + form = urllib.parse.urlencode({ + 'csrfmiddlewaretoken': token.group(1), 'username': ADMIN[0], + 'password': ADMIN[1], 'next': '/en-us/admin/'}).encode() + status, body, url = fetch( + opener, '/en-us/admin/login/', domain, data=form) + check('admin login works', + status == 200 and 'login' not in url, 'status %s, ended on %s' % (status, url)) + + print('\n{} failure(s)'.format(len(failures))) + sys.exit(1 if failures else 0) + + +main() diff --git a/utils/management/commands/seed_dummy_data.py b/utils/management/commands/seed_dummy_data.py index d50fbca6..8a8c562c 100644 --- a/utils/management/commands/seed_dummy_data.py +++ b/utils/management/commands/seed_dummy_data.py @@ -24,25 +24,13 @@ from django.contrib.sites.models import Site from django.core.management.base import BaseCommand from membership.models import CustomUser +from utils.sample_sites import SITE_TEXTS REVERSE_ID = 'dummy-home' ADMIN_EMAIL = 'dummy-admin@example.com' CUSTOMER_EMAIL_FORMAT = 'dummy-customer-{}@example.com' CUSTOMER_COUNT = 12 DEFAULT_ADMIN_PASSWORD = 'dummy-admin' -# domain -> (name, tagline); other domains are named after their domain -SITE_TEXTS = { - 'ungleich.ch': ('ungleich', 'Swiss IT infrastructure, hosting and open source.'), - 'blog.ungleich.ch': ('ungleich blog', 'Notes from running data centers, networks and free software.'), - 'comic.ungleich.ch': ('ungleich comic', 'Life in the data center, one panel at a time.'), - 'digitalglarus.ch': ('Digital Glarus', 'The Swiss IT valley in Schwanden, Glarus.'), - 'datacenterlight.ch': ('Data Center Light', 'Virtual machines from a Swiss, ecological data center.'), - 'rails-hosting.ch': ('Rails Hosting', 'Managed hosting for Ruby on Rails applications.'), - 'django-hosting.ch': ('Django Hosting', 'Managed hosting for Django applications.'), - 'node-hosting.ch': ('Node.js Hosting', 'Managed hosting for Node.js applications.'), - 'devuanhosting.ch': ('Devuan Hosting', 'Hosting on Devuan, the systemd-free Linux.'), - 'devuanhosting.com': ('Devuan Hosting', 'Hosting on Devuan, the systemd-free Linux.'), -} # (slug, title, text) for the pages below each site's home page SUB_PAGES = [ ('about', 'About', '{name} is operated by ungleich GmbH in Switzerland. ' diff --git a/utils/sample_sites.py b/utils/sample_sites.py new file mode 100644 index 00000000..09fd3b74 --- /dev/null +++ b/utils/sample_sites.py @@ -0,0 +1,15 @@ +"""Sample site names and taglines, shared by the seeder and smoke_test.py. +No Django imports, so it works anywhere.""" +# domain -> (name, tagline); other domains are named after their domain +SITE_TEXTS = { + 'ungleich.ch': ('ungleich', 'Swiss IT infrastructure, hosting and open source.'), + 'blog.ungleich.ch': ('ungleich blog', 'Notes from running data centers, networks and free software.'), + 'comic.ungleich.ch': ('ungleich comic', 'Life in the data center, one panel at a time.'), + 'digitalglarus.ch': ('Digital Glarus', 'The Swiss IT valley in Schwanden, Glarus.'), + 'datacenterlight.ch': ('Data Center Light', 'Virtual machines from a Swiss, ecological data center.'), + 'rails-hosting.ch': ('Rails Hosting', 'Managed hosting for Ruby on Rails applications.'), + 'django-hosting.ch': ('Django Hosting', 'Managed hosting for Django applications.'), + 'node-hosting.ch': ('Node.js Hosting', 'Managed hosting for Node.js applications.'), + 'devuanhosting.ch': ('Devuan Hosting', 'Hosting on Devuan, the systemd-free Linux.'), + 'devuanhosting.com': ('Devuan Hosting', 'Hosting on Devuan, the systemd-free Linux.'), +}