nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at devShellTools-shell 209 lines 7.2 kB view raw
1# This tests Discourse by: 2# 1. logging in as the admin user 3# 2. sending a private message to the admin user through the API 4# 3. replying to that message via email. 5 6{ 7 package, 8 pkgs, 9 lib, 10 ... 11}: 12let 13 certs = import ./common/acme/server/snakeoil-certs.nix; 14 clientDomain = "client.fake.domain"; 15 discourseDomain = certs.domain; 16 adminPassword = "eYAX85qmMJ5GZIHLaXGDAoszD7HSZp5d"; 17 secretKeyBase = "381f4ac6d8f5e49d804dae72aa9c046431d2f34c656a705c41cd52fed9b4f6f76f51549f0b55db3b8b0dded7a00d6a381ebe9a4367d2d44f5e743af6628b4d42"; 18 admin = { 19 email = "alice@${clientDomain}"; 20 username = "alice"; 21 fullName = "Alice Admin"; 22 passwordFile = "${pkgs.writeText "admin-pass" adminPassword}"; 23 }; 24in 25{ 26 name = "discourse"; 27 meta.maintainers = with lib.maintainers; [ talyz ]; 28 29 _module.args.package = lib.mkDefault pkgs.discourse; 30 31 nodes.discourse = 32 { nodes, ... }: 33 { 34 virtualisation.memorySize = 2048; 35 virtualisation.cores = 4; 36 virtualisation.useNixStoreImage = true; 37 virtualisation.writableStore = false; 38 39 imports = [ common/user-account.nix ]; 40 41 security.pki.certificateFiles = [ 42 certs.ca.cert 43 ]; 44 45 networking.extraHosts = '' 46 127.0.0.1 ${discourseDomain} 47 ${nodes.client.networking.primaryIPAddress} ${clientDomain} 48 ''; 49 50 services.postfix = { 51 enableSubmission = true; 52 enableSubmissions = true; 53 submissionsOptions = { 54 smtpd_sasl_auth_enable = "yes"; 55 smtpd_client_restrictions = "permit"; 56 }; 57 }; 58 59 environment.systemPackages = [ pkgs.jq ]; 60 61 services.postgresql.package = pkgs.postgresql_15; 62 63 services.discourse = { 64 enable = true; 65 inherit admin; 66 hostname = discourseDomain; 67 sslCertificate = "${certs.${discourseDomain}.cert}"; 68 sslCertificateKey = "${certs.${discourseDomain}.key}"; 69 secretKeyBaseFile = "${pkgs.writeText "secret-key-base" secretKeyBase}"; 70 enableACME = false; 71 mail.outgoing.serverAddress = clientDomain; 72 mail.incoming.enable = true; 73 siteSettings = { 74 posting = { 75 min_post_length = 5; 76 min_first_post_length = 5; 77 min_personal_message_post_length = 5; 78 }; 79 }; 80 unicornTimeout = 900; 81 }; 82 83 networking.firewall.allowedTCPPorts = [ 84 25 85 465 86 ]; 87 }; 88 89 nodes.client = 90 { nodes, ... }: 91 { 92 imports = [ common/user-account.nix ]; 93 94 security.pki.certificateFiles = [ 95 certs.ca.cert 96 ]; 97 98 networking.extraHosts = '' 99 127.0.0.1 ${clientDomain} 100 ${nodes.discourse.networking.primaryIPAddress} ${discourseDomain} 101 ''; 102 103 services.dovecot2 = { 104 enable = true; 105 protocols = [ "imap" ]; 106 }; 107 108 services.postfix = { 109 enable = true; 110 origin = clientDomain; 111 relayDomains = [ clientDomain ]; 112 config = { 113 compatibility_level = "2"; 114 smtpd_banner = "ESMTP server"; 115 myhostname = clientDomain; 116 mydestination = clientDomain; 117 }; 118 }; 119 120 environment.systemPackages = 121 let 122 replyToEmail = pkgs.writeScriptBin "reply-to-email" '' 123 #!${pkgs.python3.interpreter} 124 import imaplib 125 import smtplib 126 import ssl 127 import email.header 128 from email import message_from_bytes 129 from email.message import EmailMessage 130 131 with imaplib.IMAP4('localhost') as imap: 132 imap.login('alice', 'foobar') 133 imap.select() 134 status, data = imap.search(None, 'ALL') 135 assert status == 'OK' 136 137 nums = data[0].split() 138 assert len(nums) == 1 139 140 status, msg_data = imap.fetch(nums[0], '(RFC822)') 141 assert status == 'OK' 142 143 msg = email.message_from_bytes(msg_data[0][1]) 144 subject = str(email.header.make_header(email.header.decode_header(msg['Subject']))) 145 reply_to = email.header.decode_header(msg['Reply-To'])[0][0] 146 message_id = email.header.decode_header(msg['Message-ID'])[0][0] 147 date = email.header.decode_header(msg['Date'])[0][0] 148 149 ctx = ssl.create_default_context() 150 with smtplib.SMTP_SSL(host='${discourseDomain}', context=ctx) as smtp: 151 reply = EmailMessage() 152 reply['Subject'] = 'Re: ' + subject 153 reply['To'] = reply_to 154 reply['From'] = 'alice@${clientDomain}' 155 reply['In-Reply-To'] = message_id 156 reply['References'] = message_id 157 reply['Date'] = date 158 reply.set_content("Test reply.") 159 160 smtp.send_message(reply) 161 smtp.quit() 162 ''; 163 in 164 [ replyToEmail ]; 165 166 networking.firewall.allowedTCPPorts = [ 25 ]; 167 }; 168 169 testScript = 170 { nodes }: 171 let 172 request = builtins.toJSON { 173 title = "Private message"; 174 raw = "This is a test message."; 175 target_recipients = admin.username; 176 archetype = "private_message"; 177 }; 178 in 179 '' 180 discourse.start() 181 client.start() 182 183 discourse.wait_for_unit("discourse.service") 184 discourse.wait_for_file("/run/discourse/sockets/unicorn.sock") 185 discourse.wait_until_succeeds("curl -sS -f https://${discourseDomain}") 186 discourse.succeed( 187 "curl -sS -f https://${discourseDomain}/session/csrf -c cookie -b cookie -H 'Accept: application/json' | jq -r '\"X-CSRF-Token: \" + .csrf' > csrf_token", 188 "curl -sS -f https://${discourseDomain}/session -c cookie -b cookie -H @csrf_token -H 'Accept: application/json' -d 'login=${nodes.discourse.services.discourse.admin.username}' -d \"password=${adminPassword}\" | jq -e '.user.username == \"${nodes.discourse.services.discourse.admin.username}\"'", 189 "curl -sS -f https://${discourseDomain}/login -v -H 'Accept: application/json' -c cookie -b cookie 2>&1 | grep ${nodes.discourse.services.discourse.admin.username}", 190 ) 191 192 client.wait_for_unit("postfix.service") 193 client.wait_for_unit("dovecot2.service") 194 195 discourse.succeed( 196 "sudo -u discourse discourse-rake api_key:create_master[master] >api_key", 197 'curl -sS -f https://${discourseDomain}/posts -X POST -H "Content-Type: application/json" -H "Api-Key: $(<api_key)" -H "Api-Username: system" -d \'${request}\' ', 198 ) 199 200 client.wait_until_succeeds("reply-to-email") 201 202 discourse.wait_until_succeeds( 203 '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' 204 ) 205 discourse.succeed( 206 '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\' ' 207 ) 208 ''; 209}