feca-mailshots-plugin/tests/integration/test_phase3_db_access.php

191 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
require_once dirname(__DIR__, 2) . '/feca_mailshots_plugin/src/autoload.php';
use FecaMailshots\Application\AttachmentService;
use FecaMailshots\Application\DataSourceService;
use FecaMailshots\Application\DslCompiler;
use FecaMailshots\Application\DslValidator;
use FecaMailshots\Application\MailshotService;
use FecaMailshots\Application\PdfAssetService;
use FecaMailshots\Domain\DslParser;
use FecaMailshots\Infrastructure\DatabaseSourceMetadataProvider;
use FecaMailshots\Infrastructure\Env;
use FecaMailshots\Infrastructure\PdoDatabaseRouter;
use FecaMailshots\Repository\AttachmentRepository;
use FecaMailshots\Repository\LastRunRepository;
use FecaMailshots\Repository\MailshotQueryRepository;
use FecaMailshots\Repository\MailshotRepository;
use FecaMailshots\Repository\PdfAssetRepository;
Env::load(dirname(__DIR__, 2) . '/credentials/.env');
$dbConfig = [
'MYSQL_HOST' => '127.0.0.1',
'MYSQL_PORT' => (string) (getenv('MYSQL_TUNNEL_LOCAL_PORT') ?: '13306'),
'MYSQL_USER' => Env::require('REMOTE_MYSQL_USER'),
'MYSQL_PASSWORD' => Env::require('REMOTE_MYSQL_PASSWORD'),
'MAILSHOTS_REMOTE_MYSQL_DB' => Env::require('MAILSHOTS_REMOTE_MYSQL_DB'),
'MEMBERS_REMOTE_MYSQL_DB' => Env::require('MEMBERS_REMOTE_MYSQL_DB'),
'FEN_REMOTE_MYSQL_DB' => Env::require('FEN_REMOTE_MYSQL_DB'),
];
$router = new PdoDatabaseRouter($dbConfig);
$metadata = new DatabaseSourceMetadataProvider($router);
$parser = new DslParser();
$validator = new DslValidator($metadata);
$compiler = new DslCompiler($metadata);
$queryRepo = new MailshotQueryRepository($router);
$mailshotRepo = new MailshotRepository($router);
$dataSourceService = new DataSourceService($queryRepo, $router, $parser, $validator, $compiler, $metadata, $mailshotRepo);
$attachmentRepo = new AttachmentRepository($router);
$pdfRepo = new PdfAssetRepository($router);
$lastRunRepo = new LastRunRepository($router);
$mailshotService = new MailshotService($mailshotRepo, $queryRepo, $attachmentRepo, $lastRunRepo, $dataSourceService);
$attachmentService = new AttachmentService($attachmentRepo);
$pdfService = new PdfAssetService($pdfRepo);
$unique = gmdate('Ymd_His') . '_' . bin2hex(random_bytes(3));
$queryName = 'phase3_ds_' . $unique;
$attachmentId = null;
$pdfId = null;
$mailshotId = null;
$queryId = null;
$lastRunId = null;
$membersDb = $router->membersDbName();
$stmt = $router->membersPdo()->prepare('SELECT table_name FROM information_schema.tables WHERE table_schema = :schema ORDER BY table_name ASC LIMIT 1');
$stmt->execute(['schema' => $membersDb]);
$firstTable = $stmt->fetchColumn();
if (!is_string($firstTable) || $firstTable === '') {
fwrite(STDERR, "No source table in members db\n");
exit(1);
}
$dsl = $membersDb . '.' . $firstTable;
try {
$saveDs = $dataSourceService->save(null, $queryName, $dsl);
if (($saveDs['ok'] ?? false) !== true) {
fwrite(STDERR, 'Failed to save datasource: ' . json_encode($saveDs) . "\n");
exit(1);
}
$queryId = (int) ($saveDs['id'] ?? 0);
$tokenProbe = $mailshotService->tokenInsertionData($queryName);
$recipientEmailField = '';
foreach (($tokenProbe['tokens'] ?? []) as $token) {
if (!is_array($token) || !isset($token['field'])) {
continue;
}
$field = (string) $token['field'];
if (stripos($field, 'email') !== false) {
$recipientEmailField = $field;
break;
}
if ($recipientEmailField === '') {
$recipientEmailField = $field;
}
}
if ($recipientEmailField === '') {
fwrite(STDERR, "Expected at least one token field for datasource\n");
exit(1);
}
$saveAtt = $attachmentService->save(null, [
'name' => 'phase3_att_' . $unique,
'file_name' => 'terms.txt',
'mime_type' => 'text/plain',
'file_bytes_base64' => base64_encode('phase3 attachment bytes'),
]);
if (($saveAtt['ok'] ?? false) !== true) {
fwrite(STDERR, 'Failed to save attachment: ' . json_encode($saveAtt) . "\n");
exit(1);
}
$attachmentId = (int) ($saveAtt['id'] ?? 0);
$savePdf = $pdfService->save(null, [
'name' => 'phase3_pdf_' . $unique,
'file_name' => 'logo.png',
'file_bytes_base64' => base64_encode('fakepngbytes'),
'width_mm' => '20',
'height_mm' => '10',
'justification' => 'left',
]);
if (($savePdf['ok'] ?? false) !== true) {
fwrite(STDERR, 'Failed to save pdf asset: ' . json_encode($savePdf) . "\n");
exit(1);
}
$pdfId = (int) ($savePdf['id'] ?? 0);
$saveMailshot = $mailshotService->save(null, [
'Purpose' => 'Phase3 Mailshot ' . $unique,
'DataSource' => $queryName,
'RecipientEmailField' => $recipientEmailField,
'Subject' => 'Subject {{ ' . strtolower($firstTable) . '_id }}',
'Message' => '<p>Hello</p>',
'AttachmentNames' => json_encode(['phase3_att_' . $unique], JSON_UNESCAPED_SLASHES),
]);
if (($saveMailshot['ok'] ?? false) !== true) {
fwrite(STDERR, 'Failed to save mailshot: ' . json_encode($saveMailshot) . "\n");
exit(1);
}
$mailshotId = (int) ($saveMailshot['id'] ?? 0);
$tokenData = $mailshotService->tokenInsertionData($queryName);
if (($tokenData['errors'] ?? []) !== []) {
fwrite(STDERR, 'Token generation failed: ' . json_encode($tokenData) . "\n");
exit(1);
}
if (count($tokenData['tokens'] ?? []) === 0) {
fwrite(STDERR, "Expected token list for datasource\n");
exit(1);
}
$lastRunId = $lastRunRepo->create([
'mailshot_id' => $mailshotId,
'data_source' => $queryName,
'row_index' => 1,
'recipient_key' => 'phase3-key',
'recipient_key_field' => 'ID',
'recipient_email_last' => 'a@example.org',
'status' => 'failed',
'error_message' => 'smtp failure',
'attempt_count' => 1,
]);
$rows = $mailshotService->lastRun($mailshotId);
if (count($rows) < 1) {
fwrite(STDERR, "Expected last_run rows\n");
exit(1);
}
$mailshotService->clearLastRun($mailshotId);
$lastRunId = null;
$rowsAfter = $mailshotService->lastRun($mailshotId);
if (count($rowsAfter) !== 0) {
fwrite(STDERR, "Expected no last_run rows after clear\n");
exit(1);
}
echo "Phase 3 DB integration test passed\n";
echo "query_name={$queryName}\n";
echo "mailshot_id={$mailshotId}\n";
} finally {
if ($mailshotId !== null) {
try { $mailshotService->delete($mailshotId); } catch (Throwable $e) {}
}
if ($attachmentId !== null) {
try { $attachmentService->delete($attachmentId); } catch (Throwable $e) {}
}
if ($pdfId !== null) {
try { $pdfService->delete($pdfId); } catch (Throwable $e) {}
}
if ($queryId !== null) {
try { $queryRepo->delete($queryId); } catch (Throwable $e) {}
}
}