This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
// Runs real service/Prisma operations in a uniquely named schema; never writes public business tables.
|
||||
import assert from 'node:assert/strict';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import pg from '../../api/node_modules/pg/lib/index.js';
|
||||
import prisma from '../../api/node_modules/@prisma/client/default.js';
|
||||
import adapter from '../../api/node_modules/@prisma/adapter-pg/dist/index.js';
|
||||
import service from '../../api/dist/sending-monitor/sending-monitor.module.js';
|
||||
|
||||
process.env.TZ = 'UTC';
|
||||
const connectionString = process.env.QA_DATABASE_URL || process.env.DATABASE_URL;
|
||||
if (!connectionString) throw Error('QA_DATABASE_URL is required');
|
||||
const schema = `qa_monitor_config_${randomUUID().replaceAll('-', '')}`;
|
||||
assert.match(schema, /^qa_monitor_config_[a-f0-9]{32}$/);
|
||||
const admin = new pg.Client({ connectionString });
|
||||
let client;
|
||||
let checks = 0;
|
||||
await admin.connect();
|
||||
try {
|
||||
await admin.query(`CREATE SCHEMA "${schema}"`);
|
||||
for (const table of [
|
||||
'SmsChannel',
|
||||
'SendingMonitorTarget',
|
||||
'SendingMonitorTargetVersion',
|
||||
'SendingMonitorRule',
|
||||
'SendingMonitorRuleVersion',
|
||||
'SendingMonitorAlert',
|
||||
'OperationLog',
|
||||
])
|
||||
await admin.query(`CREATE TABLE "${schema}"."${table}" (LIKE public."${table}" INCLUDING ALL)`);
|
||||
await admin.query(
|
||||
`INSERT INTO "${schema}"."SmsChannel" SELECT * FROM public."SmsChannel" WHERE status<>'deleted' LIMIT 1`,
|
||||
);
|
||||
const pool = new pg.Pool({ connectionString, max: 2, options: `-c search_path=${schema} -c timezone=UTC` });
|
||||
client = new prisma.PrismaClient({ adapter: new adapter.PrismaPg(pool, { schema, disposeExternalPool: true }) });
|
||||
assert.equal((await client.$queryRawUnsafe('SELECT current_schema() name'))[0].name, schema);
|
||||
const sut = new service.SendingMonitorService(client);
|
||||
const channel = await client.smsChannel.findFirstOrThrow();
|
||||
const actor = 'qa-monitor-config';
|
||||
assert.equal(await client.sendingMonitorTarget.count(), 0);
|
||||
const joined = await sut.target(channel.id, { enabled: true, version: 0 }, actor);
|
||||
assert.equal(joined[0].enabled, true);
|
||||
assert.equal(joined[0].version, 1);
|
||||
assert.equal(await client.sendingMonitorTargetVersion.count(), 1);
|
||||
checks++;
|
||||
await assert.rejects(sut.target(channel.id, { enabled: true, version: 0 }, actor), (e) => e.getStatus?.() === 409);
|
||||
assert.equal(await client.operationLog.count(), 1);
|
||||
checks++;
|
||||
const race = await Promise.allSettled([
|
||||
sut.target(channel.id, { enabled: false, version: 1 }, actor),
|
||||
sut.target(channel.id, { enabled: false, version: 1 }, actor),
|
||||
]);
|
||||
assert.equal(race.filter((r) => r.status === 'fulfilled').length, 1);
|
||||
assert.equal(race.find((r) => r.status === 'rejected').reason.getStatus(), 409);
|
||||
assert.equal(await client.sendingMonitorTargetVersion.count(), 2);
|
||||
checks++;
|
||||
await sut.target(channel.id, { enabled: true, version: 2 }, actor);
|
||||
assert.equal((await client.sendingMonitorTarget.findUniqueOrThrow({ where: { channelId: channel.id } })).version, 3);
|
||||
checks++;
|
||||
for (const [id, body, status] of [
|
||||
['missing', { enabled: true, version: 0 }, 404],
|
||||
[channel.id, { enabled: 'true', version: 0 }, 400],
|
||||
[channel.id, { enabled: true, version: -1 }, 400],
|
||||
]) {
|
||||
await assert.rejects(sut.target(id, body, actor), (e) => e.getStatus?.() === status);
|
||||
checks++;
|
||||
}
|
||||
const config = { enabled: true, minSamples: 100, thresholds: [90, 95, 98], consecutiveBad: 1, consecutiveGood: 2 };
|
||||
const body = { type: 'industry', scope: {}, config, version: 0 };
|
||||
const rules = await sut.saveRule(body, actor);
|
||||
assert.equal(rules[0].version, 1);
|
||||
assert.equal(await client.sendingMonitorRuleVersion.count(), 1);
|
||||
checks++;
|
||||
const ruleRace = await Promise.allSettled([
|
||||
sut.saveRule({ ...body, version: 1 }, actor),
|
||||
sut.saveRule({ ...body, version: 1 }, actor),
|
||||
]);
|
||||
assert.equal(ruleRace.filter((r) => r.status === 'fulfilled').length, 1);
|
||||
assert.equal(ruleRace.find((r) => r.status === 'rejected').reason.getStatus(), 409);
|
||||
assert.equal(await client.sendingMonitorRuleVersion.count(), 2);
|
||||
assert.equal(await client.operationLog.count(), 5);
|
||||
checks++;
|
||||
await assert.rejects(
|
||||
sut.saveRule({ ...body, config: { ...config, minSamples: 0 } }, actor),
|
||||
(e) => e.getStatus?.() === 400,
|
||||
);
|
||||
checks++;
|
||||
// A later audit failure must roll back both the current row and immutable version.
|
||||
await client.$executeRawUnsafe(
|
||||
`ALTER TABLE "OperationLog" ADD CONSTRAINT qa_reject CHECK ("resource"<>'sending_monitor') NOT VALID`,
|
||||
);
|
||||
await assert.rejects(sut.target(channel.id, { enabled: false, version: 3 }, actor));
|
||||
assert.equal((await client.sendingMonitorTarget.findUniqueOrThrow({ where: { channelId: channel.id } })).version, 3);
|
||||
assert.equal(await client.sendingMonitorTargetVersion.count(), 3);
|
||||
checks++;
|
||||
await assert.rejects(sut.saveRule({ ...body, version: 2 }, actor));
|
||||
assert.equal(await client.sendingMonitorRuleVersion.count(), 2);
|
||||
checks++;
|
||||
console.log(JSON.stringify({ checks, realPrisma: true, schemaIsolated: true, businessWrites: 0 }));
|
||||
} finally {
|
||||
if (client) await client.$disconnect();
|
||||
await admin.query(`DROP SCHEMA "${schema}" CASCADE`);
|
||||
await admin.end();
|
||||
}
|
||||
Reference in New Issue
Block a user