198 lines
8.1 KiB
PHP
198 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once dirname(__DIR__, 2) . '/feca_mailshots_plugin/src/autoload.php';
|
|
require_once __DIR__ . '/FakeMetadataProvider.php';
|
|
|
|
use FecaMailshots\Application\DslCompiler;
|
|
use FecaMailshots\Application\DslValidator;
|
|
use FecaMailshots\Domain\DslParser;
|
|
use FecaMailshots\Tests\Unit\FakeMetadataProvider;
|
|
|
|
$metadata = new FakeMetadataProvider([
|
|
'advertisers' => ['AdvertiserName', 'Selected', 'IsLapsed?'],
|
|
'ads' => ['ID', 'Advertiser', 'AdSize', 'Price', 'PageID', 'State', 'Notes'],
|
|
'pages' => ['ID', 'Issue', 'Page'],
|
|
'articles' => ['ID', 'PageID', 'ArticleNumber', 'Content', 'MemberName', 'Author', 'DCN', 'ArticleWords', 'OtherWords', 'OtherContent'],
|
|
'issues' => ['ID', 'IssueMonths', 'Description'],
|
|
'invoices' => ['id', 'issue', 'issue_id', 'ad_id', 'invoice_number', 'status'],
|
|
'contacts' => ['id', 'account_id', 'last_name', 'contact_email_1', 'is_fen_1', 'is_deleted'],
|
|
'accounts' => ['id', 'name', 'is_deleted'],
|
|
]);
|
|
|
|
$parser = new DslParser();
|
|
$validator = new DslValidator($metadata);
|
|
$compiler = new DslCompiler($metadata);
|
|
|
|
$cases = [
|
|
'ads and pages where issue(202605)' => [
|
|
'needle' => 's_pages.`Issue` = ?',
|
|
'params' => [202605],
|
|
],
|
|
'articles and pages where issue(202605)' => [
|
|
'needle' => 's_pages.`Issue` = ?',
|
|
'params' => [202605],
|
|
],
|
|
'advertisers and ads where selected' => [
|
|
'needle' => 'COALESCE(s_advertisers.`Selected`, 0) <> 0',
|
|
'params' => [],
|
|
],
|
|
'invoices and ads where invoice-ids(10, 11)' => [
|
|
'needle' => 's_invoices.`id` IN (?,?)',
|
|
'params' => [10, 11],
|
|
],
|
|
];
|
|
|
|
foreach ($cases as $dsl => $expect) {
|
|
$ast = $parser->parse($dsl);
|
|
$validation = $validator->validate($ast);
|
|
if (($validation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$dsl}: " . json_encode($validation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$compiled = $compiler->compile($ast);
|
|
$sql = (string) ($compiled['sql'] ?? '');
|
|
if (strpos($sql, $expect['needle']) === false) {
|
|
fwrite(STDERR, "Expected SQL for {$dsl} to contain {$expect['needle']}\n{$sql}\n");
|
|
exit(1);
|
|
}
|
|
if (($compiled['params'] ?? []) !== $expect['params']) {
|
|
fwrite(STDERR, "Unexpected params for {$dsl}: " . json_encode($compiled['params'] ?? []) . "\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
$projectionDsl = 'advertisers and ads where issue(125)';
|
|
$projectionAst = $parser->parse($projectionDsl);
|
|
$projectionValidation = $validator->validate($projectionAst);
|
|
if (($projectionValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$projectionDsl}: " . json_encode($projectionValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$projection = $compiler->compile($projectionAst);
|
|
$projectionSql = (string) ($projection['sql'] ?? '');
|
|
foreach ([
|
|
'advertisers.AdvertiserName',
|
|
'advertisers.Selected',
|
|
'advertisers.IsLapsed?',
|
|
'ads.ID',
|
|
'ads.Advertiser',
|
|
'ads.AdSize',
|
|
'ads.Price',
|
|
'ads.PageID',
|
|
'ads.State',
|
|
'ads.Notes',
|
|
] as $qualifiedField) {
|
|
if (strpos($projectionSql, ' AS `' . $qualifiedField . '`') === false) {
|
|
fwrite(STDERR, "Expected projection for {$projectionDsl} to include {$qualifiedField}\n{$projectionSql}\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
$quotedFieldDsl = 'advertisers where advertisers.`IsLapsed?` = true';
|
|
$quotedFieldAst = $parser->parse($quotedFieldDsl);
|
|
$quotedFieldValidation = $validator->validate($quotedFieldAst);
|
|
if (($quotedFieldValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$quotedFieldDsl}: " . json_encode($quotedFieldValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$quotedFieldSql = (string) ($compiler->compile($quotedFieldAst)['sql'] ?? '');
|
|
if (strpos($quotedFieldSql, 's_advertisers.`IsLapsed?` = ?') === false) {
|
|
fwrite(STDERR, "Expected quoted physical field to compile directly\n{$quotedFieldSql}\n");
|
|
exit(1);
|
|
}
|
|
|
|
$issueCases = [
|
|
'issues where issue(202605)' => 's_issues.`ID` = ?',
|
|
'articles where issue(202605)' => 'p_article_issue.`Issue` = ?',
|
|
'ads where issue(202605)' => 'p_ad_issue.`Issue` = ?',
|
|
'advertisers where issue(202605)' => 'p_adv_issue.`Issue` = ?',
|
|
'invoices where issue(202605)' => 's_invoices.`issue_id` = ?',
|
|
'contacts and accounts and issues where fen1-contact and issue(202605)' => 'CROSS JOIN `issues` AS s_issues',
|
|
];
|
|
foreach ($issueCases as $dsl => $needle) {
|
|
$ast = $parser->parse($dsl);
|
|
$validation = $validator->validate($ast);
|
|
if (($validation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$dsl}: " . json_encode($validation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$sql = (string) ($compiler->compile($ast)['sql'] ?? '');
|
|
if (strpos($sql, $needle) === false) {
|
|
fwrite(STDERR, "Expected issue filter SQL for {$dsl} to contain {$needle}\n{$sql}\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
$softDeleteDsl = 'contacts and accounts where fen1-contact';
|
|
$softDeleteAst = $parser->parse($softDeleteDsl);
|
|
$softDeleteValidation = $validator->validate($softDeleteAst);
|
|
if (($softDeleteValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$softDeleteDsl}: " . json_encode($softDeleteValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$softDeleteSql = (string) ($compiler->compile($softDeleteAst)['sql'] ?? '');
|
|
foreach ([
|
|
'(s_contacts.`is_deleted` IS NULL OR s_contacts.`is_deleted` + 0 = 0)',
|
|
'(s_accounts.`is_deleted` IS NULL OR s_accounts.`is_deleted` + 0 = 0)',
|
|
] as $needle) {
|
|
if (strpos($softDeleteSql, $needle) === false) {
|
|
fwrite(STDERR, "Expected soft-delete predicate {$needle}\n{$softDeleteSql}\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
$articleExclusionDsl = 'contacts and accounts and issues where fen1-contact and issue(125) and member-or-affiliate-or-parish-council and not account-has-article-in-issue(125)';
|
|
$articleExclusionAst = $parser->parse($articleExclusionDsl);
|
|
$articleExclusionValidation = $validator->validate($articleExclusionAst);
|
|
if (($articleExclusionValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$articleExclusionDsl}: " . json_encode($articleExclusionValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$articleExclusionCompiled = $compiler->compile($articleExclusionAst);
|
|
$articleExclusionSql = (string) ($articleExclusionCompiled['sql'] ?? '');
|
|
foreach ([
|
|
'NOT (EXISTS (SELECT 1 FROM `articles` AS article_account_issue',
|
|
'article_account_issue.`MemberName`',
|
|
'COLLATE utf8mb4_unicode_ci',
|
|
'page_account_issue.`Issue` = ?',
|
|
] as $needle) {
|
|
if (strpos($articleExclusionSql, $needle) === false) {
|
|
fwrite(STDERR, "Expected article exclusion SQL to contain {$needle}\n{$articleExclusionSql}\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
if (($articleExclusionCompiled['params'] ?? []) !== [125, 125]) {
|
|
fwrite(STDERR, "Unexpected article exclusion params: " . json_encode($articleExclusionCompiled['params'] ?? []) . "\n");
|
|
exit(1);
|
|
}
|
|
|
|
$blankDsl = 'advertisers where advertisers.AdvertiserName blank';
|
|
$blankAst = $parser->parse($blankDsl);
|
|
$blankValidation = $validator->validate($blankAst);
|
|
if (($blankValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$blankDsl}: " . json_encode($blankValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$blankSql = (string) ($compiler->compile($blankAst)['sql'] ?? '');
|
|
if (strpos($blankSql, "(s_advertisers.`AdvertiserName` IS NULL OR s_advertisers.`AdvertiserName` = '')") === false) {
|
|
fwrite(STDERR, "Expected blank field test to compile as null-or-empty check\n{$blankSql}\n");
|
|
exit(1);
|
|
}
|
|
|
|
$notBlankDsl = 'advertisers where advertisers.AdvertiserName != blank';
|
|
$notBlankAst = $parser->parse($notBlankDsl);
|
|
$notBlankValidation = $validator->validate($notBlankAst);
|
|
if (($notBlankValidation['errors'] ?? []) !== []) {
|
|
fwrite(STDERR, "Expected valid DSL {$notBlankDsl}: " . json_encode($notBlankValidation['errors']) . "\n");
|
|
exit(1);
|
|
}
|
|
$notBlankSql = (string) ($compiler->compile($notBlankAst)['sql'] ?? '');
|
|
if (strpos($notBlankSql, "(s_advertisers.`AdvertiserName` IS NOT NULL AND s_advertisers.`AdvertiserName` <> '')") === false) {
|
|
fwrite(STDERR, "Expected != blank field test to compile as not-null-and-not-empty check\n{$notBlankSql}\n");
|
|
exit(1);
|
|
}
|
|
|
|
echo "DSL FEN built-in tests passed\n";
|