nixpkgs/nixos/tests/discourse.nix
talyz 2781f84bce discourse.tests: Improve test performance
Massively reduce the time it takes running the test by building a
proper root disk image and increasing the virtualized core count to
4. This should make it much easier for the tests to pass even on
weaker systems.

With my laptop (AMD Ryzen 7 PRO 2700U, 32GB RAM) as the reference
system, I see the following test run times:

- No change:
  25 mins, 49 secs

- Building a root image:
  4 mins, 44 secs

- Building a root image and bumping the core count:
  3 mins, 6 secs

The times include the time it takes to build the image (~40 secs).
2021-10-28 12:55:01 +02:00

201 lines
7.4 KiB
Nix

# This tests Discourse by:
# 1. logging in as the admin user
# 2. sending a private message to the admin user through the API
# 3. replying to that message via email.
import ./make-test-python.nix (
{ pkgs, lib, package ? pkgs.discourse, ... }:
let
certs = import ./common/acme/server/snakeoil-certs.nix;
clientDomain = "client.fake.domain";
discourseDomain = certs.domain;
adminPassword = "eYAX85qmMJ5GZIHLaXGDAoszD7HSZp5d";
secretKeyBase = "381f4ac6d8f5e49d804dae72aa9c046431d2f34c656a705c41cd52fed9b4f6f76f51549f0b55db3b8b0dded7a00d6a381ebe9a4367d2d44f5e743af6628b4d42";
admin = {
email = "alice@${clientDomain}";
username = "alice";
fullName = "Alice Admin";
passwordFile = "${pkgs.writeText "admin-pass" adminPassword}";
};
in
{
name = "discourse";
meta = with pkgs.lib.maintainers; {
maintainers = [ talyz ];
};
nodes.discourse =
{ nodes, ... }:
{
virtualisation.memorySize = 2048;
virtualisation.cores = 4;
virtualisation.useNixStoreImage = true;
imports = [ common/user-account.nix ];
security.pki.certificateFiles = [
certs.ca.cert
];
networking.extraHosts = ''
127.0.0.1 ${discourseDomain}
${nodes.client.config.networking.primaryIPAddress} ${clientDomain}
'';
services.postfix = {
enableSubmission = true;
enableSubmissions = true;
submissionsOptions = {
smtpd_sasl_auth_enable = "yes";
smtpd_client_restrictions = "permit";
};
};
environment.systemPackages = [ pkgs.jq ];
services.postgresql.package = pkgs.postgresql_13;
services.discourse = {
enable = true;
inherit admin package;
hostname = discourseDomain;
sslCertificate = "${certs.${discourseDomain}.cert}";
sslCertificateKey = "${certs.${discourseDomain}.key}";
secretKeyBaseFile = "${pkgs.writeText "secret-key-base" secretKeyBase}";
enableACME = false;
mail.outgoing.serverAddress = clientDomain;
mail.incoming.enable = true;
siteSettings = {
posting = {
min_post_length = 5;
min_first_post_length = 5;
min_personal_message_post_length = 5;
};
};
unicornTimeout = 900;
};
networking.firewall.allowedTCPPorts = [ 25 465 ];
};
nodes.client =
{ nodes, ... }:
{
imports = [ common/user-account.nix ];
security.pki.certificateFiles = [
certs.ca.cert
];
networking.extraHosts = ''
127.0.0.1 ${clientDomain}
${nodes.discourse.config.networking.primaryIPAddress} ${discourseDomain}
'';
services.dovecot2 = {
enable = true;
protocols = [ "imap" ];
modules = [ pkgs.dovecot_pigeonhole ];
};
services.postfix = {
enable = true;
origin = clientDomain;
relayDomains = [ clientDomain ];
config = {
compatibility_level = "2";
smtpd_banner = "ESMTP server";
myhostname = clientDomain;
mydestination = clientDomain;
};
};
environment.systemPackages =
let
replyToEmail = pkgs.writeScriptBin "reply-to-email" ''
#!${pkgs.python3.interpreter}
import imaplib
import smtplib
import ssl
import email.header
from email import message_from_bytes
from email.message import EmailMessage
with imaplib.IMAP4('localhost') as imap:
imap.login('alice', 'foobar')
imap.select()
status, data = imap.search(None, 'ALL')
assert status == 'OK'
nums = data[0].split()
assert len(nums) == 1
status, msg_data = imap.fetch(nums[0], '(RFC822)')
assert status == 'OK'
msg = email.message_from_bytes(msg_data[0][1])
subject = str(email.header.make_header(email.header.decode_header(msg['Subject'])))
reply_to = email.header.decode_header(msg['Reply-To'])[0][0]
message_id = email.header.decode_header(msg['Message-ID'])[0][0]
date = email.header.decode_header(msg['Date'])[0][0]
ctx = ssl.create_default_context()
with smtplib.SMTP_SSL(host='${discourseDomain}', context=ctx) as smtp:
reply = EmailMessage()
reply['Subject'] = 'Re: ' + subject
reply['To'] = reply_to
reply['From'] = 'alice@${clientDomain}'
reply['In-Reply-To'] = message_id
reply['References'] = message_id
reply['Date'] = date
reply.set_content("Test reply.")
smtp.send_message(reply)
smtp.quit()
'';
in
[ replyToEmail ];
networking.firewall.allowedTCPPorts = [ 25 ];
};
testScript = { nodes }:
let
request = builtins.toJSON {
title = "Private message";
raw = "This is a test message.";
target_usernames = admin.username;
archetype = "private_message";
};
in ''
discourse.start()
client.start()
discourse.wait_for_unit("discourse.service")
discourse.wait_for_file("/run/discourse/sockets/unicorn.sock")
discourse.wait_until_succeeds("curl -sS -f https://${discourseDomain}")
discourse.succeed(
"curl -sS -f https://${discourseDomain}/session/csrf -c cookie -b cookie -H 'Accept: application/json' | jq -r '\"X-CSRF-Token: \" + .csrf' > csrf_token",
"curl -sS -f https://${discourseDomain}/session -c cookie -b cookie -H @csrf_token -H 'Accept: application/json' -d 'login=${nodes.discourse.config.services.discourse.admin.username}' -d \"password=${adminPassword}\" | jq -e '.user.username == \"${nodes.discourse.config.services.discourse.admin.username}\"'",
"curl -sS -f https://${discourseDomain}/login -v -H 'Accept: application/json' -c cookie -b cookie 2>&1 | grep ${nodes.discourse.config.services.discourse.admin.username}",
)
client.wait_for_unit("postfix.service")
client.wait_for_unit("dovecot2.service")
discourse.succeed(
"sudo -u discourse discourse-rake api_key:create_master[master] >api_key",
'curl -sS -f https://${discourseDomain}/posts -X POST -H "Content-Type: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" -d \'${request}\' ',
)
client.wait_until_succeeds("reply-to-email")
discourse.wait_until_succeeds(
'curl -sS -f https://${discourseDomain}/topics/private-messages/system -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .topic_list.topics[0].id != null then .topic_list.topics[0].id else null end\' >topic_id'
)
discourse.succeed(
'curl -sS -f https://${discourseDomain}/t/$(<topic_id) -H "Accept: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" | jq -e \'if .post_stream.posts[1].cooked == "<p>Test reply.</p>" then true else null end\' '
)
'';
})