HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/ai.naniguide.com/tests/Feature/TemplateResetCommandTest.php
<?php

namespace Tests\Feature;

use App\Model\FormTemplate;
use App\Model\SystemEmailTemplate;
use App\Model\SystemTemplate;
use App\Model\Template;
use App\Model\TemplateCategory;
use Illuminate\Support\Facades\Artisan;
use Tests\TestCase;

/**
 * Integration test for `php artisan template:reset`.
 *
 * The command runs `db:seed --class=TemplateSeeder` which:
 *   1. Wipes + recreates TemplateCategory rows (only Base + Extended remain
 *      under the unified taxonomy — see admin-templates-overhaul feature)
 *   2. Wipes + recreates 19 SystemTemplate rows (sender_verification_email,
 *      sign_up_form, etc.)
 *   3. Wipes + recreates 34 SystemEmailTemplate rows (13 Base + 21 Extended)
 *   4. Wipes + recreates 4 FormTemplate rows (4 Base + 0 Extended; the canonical
 *      Popup_Form_Top/Bottom/Left/Right_Banner set)
 *   5. Resets each MailList's default templates
 *
 * This test guards the unified-taxonomy invariants so a future seeder edit
 * doesn't silently re-introduce legacy categories or break the Base/Extended
 * split. Critical because the admin templates UI depends on this taxonomy.
 *
 * NOTE: This test is destructive — it actually wipes and re-seeds the DB
 * tables for templates. It runs against the dev/CI database. The seeder is
 * idempotent so the post-test state is the same canonical baseline.
 */
class TemplateResetCommandTest extends TestCase
{
    public function test_template_reset_command_runs_successfully(): void
    {
        $exitCode = Artisan::call('template:reset');

        $this->assertSame(0, $exitCode, 'template:reset must exit with code 0');
    }

    public function test_template_reset_creates_only_base_and_extended_categories(): void
    {
        Artisan::call('template:reset');

        $names = TemplateCategory::orderBy('name')->pluck('name')->all();

        $this->assertSame(['Base', 'Extended'], $names,
            'TemplateCategory rows must be exactly [Base, Extended] under the unified taxonomy. ' .
            'Legacy Basic / Featured / Popup categories were dropped — do NOT re-introduce them. ' .
            'Found: ' . implode(', ', $names));
    }

    public function test_template_reset_seeds_34_system_email_templates_split_13_base_and_21_extended(): void
    {
        Artisan::call('template:reset');

        $base = TemplateCategory::whereName('Base')->firstOrFail();
        $extended = TemplateCategory::whereName('Extended')->firstOrFail();

        $totalEmails = SystemEmailTemplate::count();
        $baseEmails = SystemEmailTemplate::categoryUid($base->uid)->count();
        $extEmails = SystemEmailTemplate::categoryUid($extended->uid)->count();

        $this->assertSame(34, $totalEmails, "Expected exactly 34 system email templates, got {$totalEmails}");
        $this->assertSame(13, $baseEmails,
            "Expected 13 Base email templates (Blank → Make an announcement), got {$baseEmails}. " .
            'Sentinel: the boundary between Base and Extended is "Sitewide sale".');
        $this->assertSame(21, $extEmails,
            "Expected 21 Extended email templates (Sitewide sale → Workspace AI Invite), got {$extEmails}");
        $this->assertSame($totalEmails, $baseEmails + $extEmails,
            'Every system email template must be classified as exactly one of Base or Extended (no orphans, no duplicates).');
    }

    public function test_template_reset_seeds_4_form_templates_all_classified_as_base(): void
    {
        Artisan::call('template:reset');

        $base = TemplateCategory::whereName('Base')->firstOrFail();
        $extended = TemplateCategory::whereName('Extended')->firstOrFail();

        $totalForms = FormTemplate::count();
        $baseForms = FormTemplate::categoryUid($base->uid)->count();
        $extForms = FormTemplate::categoryUid($extended->uid)->count();

        $this->assertSame(4, $totalForms,
            "Expected exactly 4 canonical Popup form templates (Top/Bottom/Left/Right Banner), got {$totalForms}. " .
            'If this fails, Stage09 demo seeder may be re-introducing fake "Demo Form Template N" entries.');
        $this->assertSame(4, $baseForms,
            "Expected all 4 form templates to be classified as Base, got {$baseForms} Base. " .
            'Per user spec, form templates start as Base — admins create Extended ones via the picker.');
        $this->assertSame(0, $extForms,
            "Expected 0 Extended form templates after a fresh reset, got {$extForms} Extended");
    }

    public function test_template_reset_seeds_canonical_form_template_names(): void
    {
        Artisan::call('template:reset');

        $names = FormTemplate::orderBy('id')->pluck('name')->all();

        // The 4th-position form template name has a typo in the source ("Popup Form    Banner"
        // with extra spaces) — this is preserved intentionally so the test reflects reality.
        $this->assertContains('Popup Form Right Banner', $names);
        $this->assertContains('Popup Form Top Banner', $names);
        $this->assertContains('Popup Form Bottom Banner', $names);
        $this->assertCount(4, $names, 'Exactly 4 form templates expected');
    }

    public function test_template_reset_seeds_system_templates(): void
    {
        Artisan::call('template:reset');

        $count = SystemTemplate::count();

        // The exact count depends on the templateMetas array in TemplateSeeder::resetSystemTemplates()
        // — currently 19 (sender_verification_email, sign_up_form, sign_up_confirmation_*,
        // unsubscribe_*, profile_update_*, default_campaign_api_template, pricing_page, ...).
        // Use a lower bound so adding new system templates doesn't break this test.
        $this->assertGreaterThanOrEqual(15, $count,
            "Expected at least 15 system templates, got {$count}");
    }

    public function test_template_reset_is_idempotent(): void
    {
        // First run
        Artisan::call('template:reset');
        $emailsAfter1 = SystemEmailTemplate::count();
        $formsAfter1 = FormTemplate::count();
        $catsAfter1 = TemplateCategory::count();

        // Second run — should produce identical counts (reset semantics)
        Artisan::call('template:reset');
        $emailsAfter2 = SystemEmailTemplate::count();
        $formsAfter2 = FormTemplate::count();
        $catsAfter2 = TemplateCategory::count();

        $this->assertSame($emailsAfter1, $emailsAfter2,
            "SystemEmailTemplate count drifted across reset runs ({$emailsAfter1} → {$emailsAfter2}); seeder is not idempotent");
        $this->assertSame($formsAfter1, $formsAfter2,
            "FormTemplate count drifted across reset runs ({$formsAfter1} → {$formsAfter2})");
        $this->assertSame($catsAfter1, $catsAfter2,
            "TemplateCategory count drifted across reset runs ({$catsAfter1} → {$catsAfter2})");
    }

    public function test_template_reset_does_not_re_introduce_legacy_categories(): void
    {
        Artisan::call('template:reset');

        // Belt-and-suspenders: even if the test above passes, also explicitly check
        // that the legacy names are absent. A future seeder edit that adds Basic
        // alongside Base must fail this test.
        $legacyNames = ['Basic', 'Featured', 'Popup'];

        foreach ($legacyNames as $legacy) {
            $exists = TemplateCategory::whereName($legacy)->exists();
            $this->assertFalse($exists,
                "Legacy TemplateCategory '{$legacy}' must NOT exist under the unified taxonomy. " .
                'See docs/automated/features/admin-templates-overhaul.md');
        }
    }

    public function test_every_system_email_template_has_a_valid_template_relation(): void
    {
        Artisan::call('template:reset');

        $orphans = SystemEmailTemplate::with('template')
            ->get()
            ->filter(fn ($t) => $t->template === null)
            ->count();

        $this->assertSame(0, $orphans,
            "Found {$orphans} SystemEmailTemplate rows with NULL template relation. " .
            'Every wrapper must point to a real Template row.');
    }

    public function test_every_form_template_has_a_valid_template_relation(): void
    {
        Artisan::call('template:reset');

        $orphans = FormTemplate::with('template')
            ->get()
            ->filter(fn ($t) => $t->template === null)
            ->count();

        $this->assertSame(0, $orphans,
            "Found {$orphans} FormTemplate rows with NULL template relation");
    }
}