feat(quick-260914-eym): forSystem(), is_system_context(), Systemleseregel auf fuenf Tabellen, DKV-Planer je Mandant — ein Pfad (WINDOWS #21)
- Helfer forSystem(prisma) in prisma-tenant.extension.ts (Array-Form, setzt app.system_context='true' und die beiden anderen Variablen ausdruecklich leer); forTenant()/withTenantTransaction() setzen app.system_context='' als Literal (4 neue Spec-Tests) - Migration 20260914120000_rls_system_context_read: is_system_context() (COALESCE, STABLE) und system_read_policy FOR SELECT auf DkvModuleConfig, LdapConfig, LdapFieldMapping, TenderMatch, TenderSavedSearch — lokal angewendet (36 Migrationen, pg_proc 1, 5 system_read_policy, 34 Regeln) - migration-sql.spec.ts: describe-Block fuer die neue Migration (6 Tests) - rls-scratch-check.mjs: Funktion aus der Migration geschnitten, forSystemQuery/buildInlineSystemClient, Reset in forTenantQuery/ buildInlineExtendedClient, runSystemContextChecks (4 Funktionsfaelle + 9 Kennungen DkvModuleConfig) -> Alle 216 Pruefungen bestanden - rls-access-inventory.spec.ts: fuenfte Erkennungsform const X = forSystem(, Stand system-gebunden mit Vorrangregel, FORSYSTEM_ALLOWED_CALL_SITES (exakte Zahl je Datei, 3 Tests), Proben C/D/E - DKV: loadActiveConfigsForScheduler() ueber forSystem (findMany isActive, CONFIG_SAFE_SELECT, orderBy tenantId); DkvSchedulerService mit Auftrag je Mandant dkv-inbox-poll:<tenantId>, activeTenantId ersatzlos entfernt, setInterval/stopJob je Mandant, registeredTenantIds(); Controller stopJob(tenantId); neue dkv-scheduler.service.spec.ts (7 Tests), dkv.service.spec.ts Tests 6/7 umgestellt - Klassifikation: dkv.service.ts/dkvModuleConfig system-gebunden, Header mit fuenfter Erkennungsform und viertem Stand-Wert - Baseline: 63 Dateien / 1051 Tests, tsc 0, Werkzeug 216 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
@@ -22,6 +22,8 @@ import { DkvService } from './dkv.service';
|
||||
|
||||
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
||||
forTenant: vi.fn((prisma: any, tenantId: string) => prisma.__makeBoundClient(tenantId)),
|
||||
// Systemkontext (260914-eym): der Planer-Startpfad liest ueber forSystem().
|
||||
forSystem: vi.fn((prisma: any) => prisma.__makeSystemClient()),
|
||||
}));
|
||||
|
||||
// `import * as fs from 'fs'` under ESM has a non-configurable module
|
||||
@@ -45,6 +47,7 @@ function _applySelect(row: any, select: Record<string, boolean> | undefined) {
|
||||
function makeFakePrisma() {
|
||||
const configs = new Map<string, any>(); // key: tenantId
|
||||
const boundCallLog: { tenantId: string; model: string; method: string }[] = [];
|
||||
const systemCallLog: { model: string; method: string }[] = [];
|
||||
|
||||
const dkvModuleConfig = {
|
||||
findFirst: vi.fn(async ({ select }: { select?: Record<string, boolean> } = {}) => {
|
||||
@@ -215,6 +218,7 @@ function makeFakePrisma() {
|
||||
dkvVehicleMaster,
|
||||
dkvInvoiceHistory,
|
||||
__boundCallLog: boundCallLog,
|
||||
__systemCallLog: systemCallLog,
|
||||
__seedConfig(tenantId: string, row: Record<string, unknown>) {
|
||||
configs.set(tenantId, { tenantId, ...row });
|
||||
},
|
||||
@@ -231,6 +235,40 @@ function makeFakePrisma() {
|
||||
...row,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Systemkontext-Klient (260914-eym): protokolliert in __systemCallLog,
|
||||
* NICHT in __boundCallLog. dkvModuleConfig.findMany filtert ueber die
|
||||
* Map nach where.isActive und liefert nach tenantId sortiert.
|
||||
*/
|
||||
__makeSystemClient() {
|
||||
return {
|
||||
dkvModuleConfig: {
|
||||
findMany: async ({
|
||||
where,
|
||||
select,
|
||||
orderBy,
|
||||
}: {
|
||||
where?: { isActive?: boolean };
|
||||
select?: Record<string, boolean>;
|
||||
orderBy?: { tenantId?: 'asc' | 'desc' };
|
||||
} = {}) => {
|
||||
systemCallLog.push({ model: 'dkvModuleConfig', method: 'findMany' });
|
||||
let rows = Array.from(configs.values());
|
||||
if (where && typeof where.isActive === 'boolean') {
|
||||
rows = rows.filter((r) => r.isActive === where.isActive);
|
||||
}
|
||||
if (orderBy?.tenantId) {
|
||||
rows = rows.sort((a, b) =>
|
||||
orderBy.tenantId === 'asc'
|
||||
? a.tenantId.localeCompare(b.tenantId)
|
||||
: b.tenantId.localeCompare(a.tenantId),
|
||||
);
|
||||
}
|
||||
return rows.map((r) => _applySelect(r, select));
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
__makeBoundClient(tenantId: string) {
|
||||
const wrapModel = (model: Record<string, any>, modelName: string, methods: string[]) => {
|
||||
const wrapped: any = {};
|
||||
@@ -409,33 +447,45 @@ describe('DkvService — Bindung an forTenant() (260909-mir)', () => {
|
||||
expect(result.status).toBe('ok');
|
||||
});
|
||||
|
||||
it('Test 6: der bewusst uebergreifende Planer-Startpfad steht NICHT im Bindungsprotokoll — Fehlen der Bindung ist hier die bestandene Erwartung, NICHT spaeter "reparieren"', async () => {
|
||||
it('Test 6 (umgedreht, 260914-eym): der Planer-Startpfad erzeugt GENAU EINEN System-Aufruf (dkvModuleConfig.findMany) und KEINEN gebundenen — WINDOWS #21 geschlossen', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
prisma.__seedConfig('t1', { id: 'cfg-1', protocol: 'imap', isActive: true, encryptedInboxCreds: 'enc(egal)' });
|
||||
const { service } = makeDkvService(prisma);
|
||||
|
||||
await service.loadAnyActiveConfigForScheduler();
|
||||
await service.loadActiveConfigsForScheduler();
|
||||
|
||||
expect(prisma.__systemCallLog).toEqual([{ model: 'dkvModuleConfig', method: 'findMany' }]);
|
||||
expect(
|
||||
prisma.__boundCallLog.length,
|
||||
`der Planer-Startpfad darf KEINEN gebundenen Aufruf erzeugen, gefunden: ${JSON.stringify(prisma.__boundCallLog)}`,
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
it('Test 7: der Planer-Startpfad liefert die verschluesselten Zugangsdaten NICHT mit (Befund D — Entlastung wird festgeschrieben, nicht geglaubt)', async () => {
|
||||
it('Test 7: der Planer-Startpfad liefert die verschluesselten Zugangsdaten NICHT mit und genau die aktiven Mandanten, nach tenantId sortiert (260914-eym)', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
prisma.__seedConfig('t2', {
|
||||
id: 'cfg-2',
|
||||
protocol: 'imap',
|
||||
isActive: true,
|
||||
pollIntervalMin: 30,
|
||||
encryptedInboxCreds: 'enc(sollte-nie-hier-auftauchen)',
|
||||
});
|
||||
prisma.__seedConfig('t3', { id: 'cfg-3', protocol: 'imap', isActive: false, pollIntervalMin: 60 });
|
||||
prisma.__seedConfig('t1', {
|
||||
id: 'cfg-1',
|
||||
protocol: 'imap',
|
||||
isActive: true,
|
||||
pollIntervalMin: 15,
|
||||
encryptedInboxCreds: 'enc(sollte-nie-hier-auftauchen)',
|
||||
});
|
||||
const { service } = makeDkvService(prisma);
|
||||
|
||||
const result = await service.loadAnyActiveConfigForScheduler();
|
||||
const result = await service.loadActiveConfigsForScheduler();
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect((result as any).encryptedInboxCreds).toBeUndefined();
|
||||
expect(result.map((r: any) => r.tenantId)).toEqual(['t1', 't2']);
|
||||
for (const row of result) {
|
||||
expect((row as any).encryptedInboxCreds).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Aufgabe 3 (260909-mir): dkvVehicleMaster / dkvInvoiceHistory / getExportFile ───
|
||||
|
||||
Reference in New Issue
Block a user