Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
83 lines
2.8 KiB
JavaScript
83 lines
2.8 KiB
JavaScript
#!/usr/bin/env node
|
||
// ADR-0064 WP-A smoke — verifies cluster-wide cache eviction via pg_notify.
|
||
//
|
||
// Opens two independent `pg.Client` connections, both LISTEN on
|
||
// `chat_prefs_invalidate`. Then a third client fires `pg_notify` and we
|
||
// assert both listeners receive the payload. Mirrors how two PM2 workers
|
||
// (or PROD + DEV behind the same DB) coordinate cache invalidation.
|
||
//
|
||
// Run on DEV against godcrm_test:
|
||
// POSTGRES_DB=godcrm_test BUSINESS_CRM_IS_PROD=0 node scripts/smoke-adr-0064-pg-notify.mjs
|
||
//
|
||
// Aborts (exit 2) on a PROD host via the boot guard.
|
||
|
||
import '../backend/test/setup.js';
|
||
import pg from 'pg';
|
||
|
||
const CHANNEL = 'chat_prefs_invalidate';
|
||
|
||
function makeClient() {
|
||
return new pg.Client({
|
||
host: process.env.POSTGRES_HOST || 'localhost',
|
||
port: parseInt(process.env.POSTGRES_PORT || '5432', 10),
|
||
database: process.env.POSTGRES_DB,
|
||
user: process.env.POSTGRES_USER || 'godcrm',
|
||
password: process.env.POSTGRES_PASSWORD || 'godcrm_dev_2026',
|
||
});
|
||
}
|
||
|
||
async function main() {
|
||
const listenerA = makeClient();
|
||
const listenerB = makeClient();
|
||
const sender = makeClient();
|
||
|
||
await Promise.all([listenerA.connect(), listenerB.connect(), sender.connect()]);
|
||
|
||
const received = { A: [], B: [] };
|
||
listenerA.on('notification', (msg) => { if (msg.channel === CHANNEL) received.A.push(msg.payload); });
|
||
listenerB.on('notification', (msg) => { if (msg.channel === CHANNEL) received.B.push(msg.payload); });
|
||
|
||
await listenerA.query(`LISTEN ${CHANNEL}`);
|
||
await listenerB.query(`LISTEN ${CHANNEL}`);
|
||
|
||
// Three sample payloads matching the resolver's three invalidation shapes.
|
||
const payloads = [
|
||
{ user_id: 1, conversation_id: 100 },
|
||
{ space_id: 11 },
|
||
{ scope: 'global', key: 'chat_notifications_global' },
|
||
];
|
||
|
||
for (const p of payloads) {
|
||
await sender.query(`SELECT pg_notify($1, $2)`, [CHANNEL, JSON.stringify(p)]);
|
||
}
|
||
|
||
// PG notifications are dispatched on next message-pump tick after commit.
|
||
// Allow up to 1s for the round-trip.
|
||
await new Promise((r) => setTimeout(r, 500));
|
||
|
||
await Promise.all([listenerA.end(), listenerB.end(), sender.end()]);
|
||
|
||
const ok = received.A.length === payloads.length && received.B.length === payloads.length;
|
||
if (!ok) {
|
||
console.error('SMOKE FAILED — payload counts:', received);
|
||
process.exit(1);
|
||
}
|
||
|
||
// Verify payload integrity end-to-end on one listener.
|
||
for (let i = 0; i < payloads.length; i += 1) {
|
||
const sent = payloads[i];
|
||
const got = JSON.parse(received.A[i]);
|
||
if (JSON.stringify(sent) !== JSON.stringify(got)) {
|
||
console.error(`SMOKE FAILED — payload ${i} mismatch:`, { sent, got });
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
console.log(`[smoke-adr-0064] OK — ${payloads.length} payloads × 2 listeners delivered`);
|
||
process.exit(0);
|
||
}
|
||
|
||
main().catch((err) => {
|
||
console.error('SMOKE ERROR:', err);
|
||
process.exit(1);
|
||
});
|