File: /home/xedaptot/ai.naniguide.com/tests/Unit/SubscriberFlowTest.php
<?php
use App\Events\MailListSubscription;
use App\Events\MailListUpdated;
use App\Model\Contact;
use App\Model\Customer;
use App\Model\Form;
use App\Model\MailList;
use App\Model\MailListTemplate;
use App\Model\Subscriber;
use App\Model\Template;
use App\Services\SubscriberService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
uses(TestCase::class);
beforeEach(function () {
Config::set('sharding.enabled', false);
});
afterEach(function () {
$listIds = DB::table('mail_lists')
->where('uid', 'like', 'subflow_list_%')
->pluck('id');
$subscriberIds = DB::table('subscribers')
->whereIn('mail_list_id', $listIds)
->pluck('id');
$mailListTemplateIds = DB::table('mail_list_templates')
->whereIn('mail_list_id', $listIds)
->pluck('id');
$templateIds = DB::table('mail_list_templates')
->whereIn('id', $mailListTemplateIds)
->pluck('template_id');
DB::table('timelines')->whereIn('subscriber_id', $subscriberIds)->delete();
DB::table('forms')->whereIn('mail_list_id', $listIds)->delete();
DB::table('mail_list_templates')->whereIn('id', $mailListTemplateIds)->delete();
DB::table('subscribers')->whereIn('id', $subscriberIds)->delete();
DB::table('fields')->whereIn('mail_list_id', $listIds)->delete();
DB::table('mail_lists')->whereIn('id', $listIds)->delete();
DB::table('templates')->whereIn('id', $templateIds)->delete();
$customerIds = DB::table('customers')
->where('uid', 'like', 'subflow_customer_%')
->pluck('id');
DB::table('customers')->whereIn('id', $customerIds)->delete();
DB::table('contacts')->where('uid', 'like', 'subflow_contact_%')->delete();
});
function subscriberFlowFixture(array $listOverrides = []): array
{
$contact = Contact::forceCreate([
'uid' => uniqid('subflow_contact_'),
'first_name' => 'Subflow',
'last_name' => 'Tester',
'email' => uniqid('subflow_') . '@example.test',
]);
$customer = Customer::forceCreate([
'uid' => uniqid('subflow_customer_'),
'contact_id' => $contact->id,
'status' => 'active',
'timezone' => 'UTC',
]);
$list = MailList::forceCreate(array_merge([
'uid' => uniqid('subflow_list_'),
'name' => 'Subscriber Flow List',
'customer_id' => $customer->id,
'from_email' => '[email protected]',
'from_name' => 'Sender',
'subscribe_confirmation'=> false,
], $listOverrides));
$thankYouTemplate = Template::forceCreate([
'uid' => uniqid('subflow_tpl_'),
'name' => 'Thank You',
'content' => '<html><body><p>Thanks for confirming.</p></body></html>',
'json' => \App\Model\Template::DEFAULT_BUILDER_JSON,
]);
MailListTemplate::forceCreate([
'uid' => uniqid('subflow_mltpl_'),
'mail_list_id'=> $list->id,
'customer_id' => $customer->id,
'template_id' => $thankYouTemplate->id,
'alias' => 'sign_up_thankyou_page',
]);
$confirmationTemplate = Template::forceCreate([
'uid' => uniqid('subflow_tpl_'),
'name' => 'Confirmation Email',
'content' => '<html><body><a href="{{ SUBSCRIBE_CONFIRM_URL }}">Confirm</a></body></html>',
'json' => \App\Model\Template::DEFAULT_BUILDER_JSON,
]);
$confirmationMailTemplate = MailListTemplate::forceCreate([
'uid' => uniqid('subflow_mltpl_'),
'mail_list_id'=> $list->id,
'customer_id' => $customer->id,
'template_id' => $confirmationTemplate->id,
'alias' => 'sign_up_confirmation_email',
]);
$form = Form::forceCreate([
'uid' => uniqid('subflow_form_'),
'name' => 'Popup Form',
'status' => Form::STATUS_PUBLISHED,
'customer_id' => $customer->id,
'mail_list_id'=> $list->id,
]);
return [
'contact' => $contact,
'customer' => $customer,
'list' => $list->fresh(),
'form' => $form,
'confirmationMailTemplate' => $confirmationMailTemplate->fresh(),
'emailField' => $list->getFieldByTag('EMAIL'),
'firstNameField' => $list->getFieldByTag('FIRST_NAME'),
];
}
it('requires a signed confirmation url and confirms only once', function () {
Event::fake([MailListSubscription::class]);
$fixture = subscriberFlowFixture(['subscribe_confirmation' => true]);
$subscriber = Subscriber::forceCreate([
'uid' => uniqid('subflow_sub_'),
'email' => uniqid('confirm_') . '@example.test',
'status' => Subscriber::STATUS_UNCONFIRMED,
'mail_list_id' => $fixture['list']->id,
]);
$renderedConfirmation = $fixture['confirmationMailTemplate']->renderContent(null, $subscriber);
expect($renderedConfirmation)->toContain('signature=');
$unsignedUrl = route('lists.subscribe_confirm', [
'list_uid' => $fixture['list']->uid,
'id' => $subscriber->id,
'customer_uid' => $fixture['customer']->uid,
], true);
$this->get($unsignedUrl)->assertForbidden();
expect($subscriber->fresh()->status)->toBe(Subscriber::STATUS_UNCONFIRMED);
$signedUrl = app(SubscriberService::class)->generateSignedConfirmationUrl($subscriber);
$this->get($signedUrl)->assertOk();
expect($subscriber->fresh()->status)->toBe(Subscriber::STATUS_SUBSCRIBED);
Event::assertDispatchedTimes(MailListSubscription::class, 1);
$this->get($signedUrl)->assertOk();
Event::assertDispatchedTimes(MailListSubscription::class, 1);
});
it('preserves existing subscriber fields when web opt in omits them', function () {
Event::fake([MailListSubscription::class, MailListUpdated::class]);
$fixture = subscriberFlowFixture(['subscribe_confirmation' => false]);
$subscriber = Subscriber::forceCreate([
'uid' => uniqid('subflow_sub_'),
'email' => uniqid('resub_') . '@example.test',
'status' => Subscriber::STATUS_UNSUBSCRIBED,
'mail_list_id' => $fixture['list']->id,
]);
$subscriber->setFieldValue($fixture['firstNameField'], 'Existing Name');
$subscriber->save();
$request = Request::create('/frontend/lists/test/sign-up', 'POST', [
'EMAIL' => $subscriber->email,
]);
[$validator, $updatedSubscriber] = app(SubscriberService::class)->optInFromWeb($fixture['list'], $request);
expect($validator->fails())->toBeFalse();
expect($updatedSubscriber->id)->toBe($subscriber->id);
expect($updatedSubscriber->fresh()->getValueByField($fixture['firstNameField']))->toBe('Existing Name');
});
it('dispatches mail list updated only once for popup opt in', function () {
Event::fake([MailListUpdated::class, MailListSubscription::class]);
$fixture = subscriberFlowFixture(['subscribe_confirmation' => false]);
$response = $this->postJson('/frontend/forms/' . $fixture['form']->uid . '/save', [
'EMAIL' => uniqid('popup_') . '@example.test',
]);
$response->assertOk()->assertJson([
'message' => 'OK',
]);
Event::assertDispatchedTimes(MailListUpdated::class, 1);
Event::assertDispatchedTimes(MailListSubscription::class, 1);
});