cb80b67993
Assert that the PostgreSQL version being deployed is the one used upstream. Allow the user to override this assertion, since it's not always possible or preferable to use the recommended one.
200 lines
7.3 KiB
Nix
200 lines
7.3 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, ... }:
|
|
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;
|
|
|
|
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;
|
|
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\' '
|
|
)
|
|
'';
|
|
})
|