/ __quarantined_tests__ / data / required-files.test.js
required-files.test.js
  1  /**
  2   * Tests that all non-code data files required at runtime are present.
  3   *
  4   * If this test fails after a commit, it means a file the pipeline depends on
  5   * was accidentally deleted.  Restore it from git history.
  6   *
  7   * The manifest (REQUIRED_FILES below) is the single source of truth.
  8   * A pre-commit hook auto-adds new data/, prompts/, and docs/ files to it.
  9   */
 10  
 11  import { test, describe } from 'node:test';
 12  import assert from 'node:assert/strict';
 13  import { existsSync, readdirSync, lstatSync } from 'fs';
 14  import { join } from 'path';
 15  
 16  const ROOT = join(import.meta.dirname, '..', '..');
 17  
 18  // ── Countries defined in src/config/countries.js ────────────────────────────
 19  // Lowercase versions used for data/{cc}/ and data/franchises/{cc}.txt
 20  const COUNTRIES = [
 21    'at', 'au', 'be', 'ca', 'ch', 'cn', 'de', 'dk', 'es', 'fr',
 22    'id', 'ie', 'in', 'it', 'jp', 'kr', 'mx', 'nl', 'no', 'nz',
 23    'pl', 'se', 'sg', 'uk', 'us',
 24  ];
 25  
 26  // Uppercase versions used for data/templates/{CC}/
 27  const TEMPLATE_COUNTRIES_UPPER = [
 28    'AT', 'AU', 'BE', 'CA', 'CH', 'CN', 'DE', 'DK', 'ES', 'FR',
 29    'GB', 'ID', 'IE', 'IN', 'IT', 'JP', 'KR', 'MX', 'NL', 'NO',
 30    'NZ', 'PL', 'SE', 'US', 'ZA',
 31  ];
 32  
 33  // Countries with native-script keyword files (loaded by keyword-manager.js)
 34  const NATIVE_SCRIPT_COUNTRIES = ['cn', 'jp', 'kr'];
 35  
 36  // ── Required files manifest ─────────────────────────────────────────────────
 37  // Each entry: relative path from project root.
 38  // Grouped by category for readability; the test iterates the flat list.
 39  
 40  const REQUIRED_FILES = [
 41    // ── Prompts (loaded via readFileSync at module-init or by orchestrator) ──
 42    'prompts/AUDIT-REPORT-SCORING.md',
 43    'prompts/CONTACT-EXTRACTION.md',
 44    'prompts/CONVERSION-RESCORING.md',
 45    'prompts/CONVERSION-RESCORING-VISION.md',
 46    'prompts/CONVERSION-SCORING-NOVIS.md',
 47    'prompts/CONVERSION-SCORING-VISION.md',
 48    'prompts/ENRICHMENT.md',
 49    'prompts/ENRICHMENT-VISION.md',
 50    'prompts/EVIDENCE-COLLECT.md',
 51    'prompts/EVIDENCE-MERGE.md',
 52    'prompts/FORM-CLASSIFY-FIELDS.md',
 53    'prompts/FORM-GUESS-VALUES.md',
 54    'prompts/FORM-SELECT-BEST.md',
 55    'prompts/HAIKU-ANALYZE.md',
 56    'prompts/HAIKU-POLISH.md',
 57    'prompts/NAME-EXTRACTOR.md',
 58    'prompts/PROOFREAD.md',
 59    'prompts/PROPOSAL.md',
 60    'prompts/REPLIES.md',
 61    'prompts/VISION.md',
 62    'prompts/autoresponder.md',
 63    'prompts/autoresponder-2step.md',
 64  
 65    // ── Agent prompts (loaded by orchestrator run_checked_gated) ─────────────
 66    'prompts/agents/CHECK-DOCS.md',
 67    'prompts/agents/CODE-REVIEW.md',
 68    'prompts/agents/MONITOR-HEALTH.md',
 69    'prompts/agents/TRIAGE-ERRORS.md',
 70  
 71    // ── Compliance data (loaded at module init) ──────────────────────────────
 72    'data/compliance/requirements.json',
 73    'data/compliance/blocked-channels.json',
 74    'data/compliance/paused-languages.json',
 75  
 76    // ── Form / captcha data ──────────────────────────────────────────────────
 77    'data/form-builder-templates.json',
 78    'data/field-label-corrections.json',
 79    'data/captcha-provider-benchmark.json',
 80  
 81    // ── Best-practices docs (loaded by orchestrator as context files) ────────
 82    'docs/05-outreach/email-best-practices.md',
 83    'docs/05-outreach/sms-best-practices.md',
 84  
 85    // ── DB schema ────────────────────────────────────────────────────────────
 86    'db/schema.sql',
 87  
 88    // ── Per-country keyword CSVs (loaded by keyword-manager.js) ──────────────
 89    ...COUNTRIES.flatMap(cc => [
 90      `data/${cc}/businesses-final-filtered.csv`,
 91      `data/${cc}/regions-final-filtered.csv`,
 92    ]),
 93  
 94    // ── Native-script keyword files (JP, CN, KR only) ───────────────────────
 95    ...NATIVE_SCRIPT_COUNTRIES.flatMap(cc => [
 96      `data/${cc}/businesses-native.txt`,
 97      `data/${cc}/regions-native.txt`,
 98    ]),
 99  
100    // ── Franchise blocklists (loaded by site-filters.js) ─────────────────────
101    ...COUNTRIES.map(cc => `data/franchises/${cc}.txt`),
102  
103    // ── Per-country outreach templates (email.json + sms.json minimum) ───────
104    ...TEMPLATE_COUNTRIES_UPPER.flatMap(cc => {
105      // Detect lang-subfolder vs flat layout
106      const ccDir = join(ROOT, 'data', 'templates', cc);
107      if (!existsSync(ccDir)) return [`data/templates/${cc}/email.json`, `data/templates/${cc}/sms.json`];
108      const entries = readdirSync(ccDir);
109      const hasLangDir = entries.some(e => {
110        const full = join(ccDir, e);
111        return lstatSync(full).isDirectory();
112      });
113      if (hasLangDir) {
114        // Return lang-specific paths for each language subfolder
115        return entries
116          .filter(e => lstatSync(join(ccDir, e)).isDirectory())
117          .flatMap(lang => [
118            `data/templates/${cc}/${lang}/email.json`,
119            `data/templates/${cc}/${lang}/sms.json`,
120          ]);
121      }
122      return [`data/templates/${cc}/email.json`, `data/templates/${cc}/sms.json`];
123    }),
124  
125    // UK symlink → GB
126    'data/templates/UK',
127  ];
128  
129  // ── Tests ───────────────────────────────────────────────────────────────────
130  
131  describe('required data files', () => {
132    const missing = [];
133  
134    test('all required files exist', () => {
135      for (const rel of REQUIRED_FILES) {
136        const abs = join(ROOT, rel);
137        if (!existsSync(abs)) {
138          missing.push(rel);
139        }
140      }
141      if (missing.length > 0) {
142        assert.fail(
143          `${missing.length} required file(s) missing:\n${ 
144          missing.map(f => `  - ${f}`).join('\n') 
145          }\n\nRestore from git: git checkout HEAD~1 -- <path>`
146        );
147      }
148    });
149  
150    test('UK template symlink points to GB', () => {
151      const ukPath = join(ROOT, 'data', 'templates', 'UK');
152      assert.ok(existsSync(ukPath), 'data/templates/UK should exist');
153      assert.ok(lstatSync(ukPath).isSymbolicLink(), 'data/templates/UK should be a symlink');
154    });
155  });