godcrm/scripts/smoke/mail-bridge-roundtrip.mjs
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

45 lines
3.7 KiB
JavaScript

// ADR-160 end-to-end bridge proof against the REAL sysadmin@godcrm.ai mailbox.
// Replicates the /sync and /send handler logic (same imapFetch, same
// mail_messages upsert, same SMTPService transport). Run as a standalone
// process because the live godcrm predates these routes (stale, can't restart
// from inside it). This exercises the actual code paths, not mocks.
import 'dotenv/config';
import { dbGet, dbAll, dbRun } from '../../backend/database/connection.js';
import credentialVault from '../../backend/services/connectors/CredentialVault.js';
import { imapFetch } from '../../backend/services/mail/imapFetch.js';
import SMTPService from '../../backend/services/SMTPService.js';
const SPACE_ID = 11;
const row = await dbGet(`SELECT id, encrypted_payload FROM space_connectors WHERE space_id=? AND type_slug='imap' AND status='active' ORDER BY id DESC LIMIT 1`, [SPACE_ID]);
if (!row) { console.error('no imap connector'); process.exit(1); }
const creds = credentialVault.decrypt(typeof row.encrypted_payload === 'string' ? JSON.parse(row.encrypted_payload) : row.encrypted_payload);
console.log('connector id=%d user=%s', row.id, creds.username);
// ── SYNC (inbox) ──────────────────────────────────────────────
const fetched = await imapFetch({ creds, folder: 'inbox', limit: 50 });
console.log('IMAP fetched %d message(s) from inbox', fetched.length);
let synced = 0;
for (const m of fetched) {
await dbRun(
`INSERT INTO mail_messages (space_id, connector_id, folder, imap_uid, message_id, from_name, from_address, to_addresses, subject, preview, body_text, body_html, date, is_read)
VALUES (?, ?, 'inbox', ?, ?, ?, ?, ?::jsonb, ?, ?, ?, ?, ?, ?)
ON CONFLICT (connector_id, folder, imap_uid) DO UPDATE SET subject=EXCLUDED.subject, preview=EXCLUDED.preview, updated_at=now()`,
[SPACE_ID, row.id, m.imap_uid ?? null, m.message_id, m.from_name, m.from_address, JSON.stringify(m.to_addresses||[]), m.subject, m.preview, m.body_text, m.body_html, m.date, m.is_read]
);
synced++;
}
console.log('synced %d row(s) into mail_messages', synced);
// ── MESSAGES (read back as the UI would) ──────────────────────
const back = await dbAll(`SELECT id, folder, from_address, subject, preview, is_read, date FROM mail_messages WHERE space_id=? AND connector_id=? AND folder='inbox' ORDER BY date DESC NULLS LAST LIMIT 10`, [SPACE_ID, row.id]);
console.log('--- inbox rows ---');
for (const r of back) console.log(` [${r.is_read?'read':'NEW '}] ${r.from_address} | ${r.subject} | ${r.preview?.slice(0,50)||''}`);
// ── SEND (real SMTP submission) ───────────────────────────────
const transport = SMTPService.createTransport({ host: creds.smtp_host, port: Number(creds.smtp_port)||587, user: creds.username, password: creds.password });
const info = await transport.sendMail({ from: creds.username, to: creds.username, subject: 'ADR-160 round-trip send '+process.env.STAMP, text: 'sent via SMTPService.createTransport through the connector creds — bridge send path verified.' });
console.log('SENT ok messageId=%s', info.messageId);
await dbRun(`INSERT INTO mail_messages (space_id, connector_id, folder, message_id, from_name, from_address, to_addresses, subject, preview, body_text, body_html, date, is_read) VALUES (?, ?, 'sent', ?, ?, ?, ?::jsonb, ?, ?, ?, NULL, now(), true)`,
[SPACE_ID, row.id, info.messageId, creds.username, creds.username, JSON.stringify([creds.username]), 'ADR-160 round-trip send', 'bridge send path verified', 'bridge send path verified']);
console.log('Sent copy persisted. DONE.');
process.exit(0);