host = getenv('MAILPIT_HOST'); if ($this->host === false || $this->host === '') { $this->markTestSkipped('MAILPIT_HOST is not set, skipping the Mailpit integration test'); } $this->smtpPort = (int)(getenv('MAILPIT_SMTP_PORT') ?: 1025); $httpPort = (int)(getenv('MAILPIT_HTTP_PORT') ?: 8025); $this->apiBase = 'http://' . $this->host . ':' . $httpPort; // a Mailpit server was requested, so it must actually be reachable $sock = @fsockopen($this->host, $this->smtpPort, $errno, $errstr, 2); if (!$sock) { $this->fail("No Mailpit SMTP server reachable at $this->host:$this->smtpPort ($errstr)"); } fclose($sock); // point the plugin at our Mailpit server global $conf; $conf['plugin']['smtp']['smtp_host'] = $this->host; $conf['plugin']['smtp']['smtp_port'] = $this->smtpPort; $conf['plugin']['smtp']['smtp_ssl'] = ''; $conf['plugin']['smtp']['auth_user'] = ''; $conf['plugin']['smtp']['auth_pass'] = ''; $conf['plugin']['smtp']['localdomain'] = 'test.localhost'; $conf['plugin']['smtp']['debug'] = 0; // give DokuWiki's Mailer a sane default sender $conf['mailfrom'] = 'wiki@example.com'; } /** * A mail sent through DokuWiki's Mailer must be delivered to Mailpit over SMTP. * * To and Cc come from the message headers, while the Bcc recipient is delivered * through the SMTP envelope even though the plugin strips the Bcc header from the * message body. */ public function testSendMail(): void { $subject = 'SMTP plugin integration test ' . uniqid('', true); $bodyText = 'Hello from the DokuWiki smtp plugin integration test.'; $mailer = new Mailer(); $mailer->from('Wiki Admin '); $mailer->to('Jane Doe '); $mailer->cc('carol@example.com'); $mailer->bcc('secret@example.com'); $mailer->subject($subject); $mailer->setBody($bodyText); $ok = $mailer->send(); $this->assertTrue($ok, 'Mailer::send() should report success when talking to Mailpit'); // the message should now be available through the Mailpit API $message = $this->findMessageBySubject($subject); $this->assertNotNull($message, 'The sent message should show up in Mailpit'); // sender and header recipients $this->assertEquals('wiki@example.com', $message['From']['Address']); $this->assertEquals(['jane@example.com'], $this->addresses($message['To'])); $this->assertEquals(['carol@example.com'], $this->addresses($message['Cc'])); $this->assertEquals(['secret@example.com'], $this->addresses($message['Bcc'])); // the body must have arrived intact $full = $this->apiGet('/api/v1/message/' . rawurlencode($message['ID'])); $this->assertStringContainsString($bodyText, $full['Text']); } /** * Reduce a Mailpit address list to a plain list of email addresses * * @param array $list list of {Name, Address} entries as returned by Mailpit * @return string[] */ protected function addresses(array $list): array { return array_map(static fn($addr) => $addr['Address'], $list); } /** * Find a message in Mailpit whose subject contains the given needle * * DokuWiki prefixes the subject with the wiki title (eg. "[My Wiki] ..."), so we * match on a unique substring rather than the full subject. Mailpit stores the * message before acknowledging the SMTP DATA command, so it is available as soon * as Mailer::send() returns - no polling needed. * * @param string $needle a unique substring of the subject * @return array|null the message stub from the listing or null if not found */ protected function findMessageBySubject($needle) { $list = $this->apiGet('/api/v1/messages'); foreach ($list['messages'] as $msg) { if (strpos($msg['Subject'], $needle) !== false) return $msg; } return null; } /** * Perform a GET request against the Mailpit API and return the decoded JSON * * @param string $path API path including the leading slash * @return array */ protected function apiGet($path) { $client = new DokuHTTPClient(); $body = $client->get($this->apiBase . $path); $this->assertNotFalse($body, 'Mailpit API request to ' . $path . ' failed: ' . $client->error); return json_decode($body, true); } }