feat(quick-260914-eym): Mail-Transport je Versand nach Mandant (WINDOWS #30), ldap/digest/matching ueber Systemkontext, vier Tabellen im Werkzeug, Erlaubnisliste vollstaendig
- mail: MailerModule-Fabrik und DB-Startpfad (findFirst beim Boot) ersatzlos entfernt; MailService baut je Versand einen nodemailer-Transport aus getDecryptedSmtpConfig(tenantId) des Empfaenger-Mandanten, Umgebungs-Kette (MAIL_* -> TESSERA_SMTP_* -> localhost:1025) nur als Rueckfall; Fehler weiter verschluckt (T-02-12), close() im finally; neue mail.service.spec.ts (4 Tests, T-GWH-03 geschlossen) - settings: Startpfad-Methode samt vier Spec-Tests geloescht; auth: requestPasswordReset reicht user.tenantId durch (Spec-Zusicherung) - ldap: getAllActiveConfigs und Nachverschluesselung lesen ueber forSystem (zwei Zuweisungen), Schreibzeile je Altzeile ueber forTenant(config.tenantId); Tests 301/306 umgedreht, neuer Altzeilen-Test - tender-digest: Kandidatenabfrage ueber forSystem, Schleife gebunden (+1 Test) - tender-matching: Profilabfrage ueber forSystem, Katalog (D-03) ungebunden (+1 Test) - tender-notifications.integration.spec: Mock um forSystem - Werkzeug: LdapConfig (15 Spalten), LdapFieldMapping (6), TenderMatch (8), TenderSavedSearch (8) je neun Kennungen plus Relations-Kennung ldapconfig-systemkontext-include-fieldmappings-beider-mandanten -> Alle 253 Pruefungen bestanden - Detektor: FORSYSTEM_ALLOWED_CALL_SITES auf 4 Dateien / 5 Aufrufe; Proben-Empfaenger sysPrisma (Gate-Zaehlung, Name nicht hartkodiert) - Klassifikation: 6 Zeilen system-gebunden, settings/smtpConfig gebunden - Falsifizierung durch Rueckbau ausgefuehrt und zurueckgenommen: (a) FOR SELECT bei TenderMatch entfernt -> 5 von 253 rot (Insert gelingt, cmd ALL); (b) Regel TenderSavedSearch aus der Datei entfernt -> 1 von 245 rot (Extraktion), lebende DB bleibt bei 34; (c) local=false -> gruen, plus Reset entfernt -> 5 rot (Erben sichtbar); (d) Zahl 0 -> 2 rot, Fremddatei admin-seed -> 3 rot - Baseline: 64 Dateien / 1054 Tests, tsc 0, Werkzeug 253 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||
import { forSystem, forTenant } from '../prisma/prisma-tenant.extension';
|
||||
import { TenderDigestScheduler } from './tender-digest.scheduler';
|
||||
|
||||
/**
|
||||
@@ -29,6 +29,8 @@ import { TenderDigestScheduler } from './tender-digest.scheduler';
|
||||
*/
|
||||
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
||||
forTenant: vi.fn((prisma: any, tenantId: string) => prisma.__makeBoundClient(tenantId)),
|
||||
// Systemkontext (260914-eym): die Kandidatenabfrage laeuft ueber forSystem().
|
||||
forSystem: vi.fn((prisma: any) => prisma.__makeSystemClient()),
|
||||
}));
|
||||
|
||||
const MONDAY = new Date('2026-07-27T10:00:00Z');
|
||||
@@ -84,6 +86,7 @@ function makeFakePrisma(opts: {
|
||||
};
|
||||
|
||||
const modelsByName: Record<string, any> = { tenderMatch, tenderNotificationPref, user };
|
||||
const systemCallLog: { model: string; method: string }[] = [];
|
||||
|
||||
return {
|
||||
tenderMatch,
|
||||
@@ -91,6 +94,19 @@ function makeFakePrisma(opts: {
|
||||
user,
|
||||
__store: { matches, prefs, users },
|
||||
__boundCallLog: boundCallLog,
|
||||
__systemCallLog: systemCallLog,
|
||||
// Systemkontext-Klient (260914-eym): protokolliert in __systemCallLog,
|
||||
// tenderMatch.findMany unveraendert (dieselbe Fake-Implementierung).
|
||||
__makeSystemClient() {
|
||||
return {
|
||||
tenderMatch: {
|
||||
findMany: async (args: any) => {
|
||||
systemCallLog.push({ model: 'tenderMatch', method: 'findMany' });
|
||||
return tenderMatch.findMany(args);
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
__makeBoundClient(tenantId: string) {
|
||||
const bound: any = {};
|
||||
for (const [modelName, model] of Object.entries(modelsByName)) {
|
||||
@@ -380,6 +396,26 @@ describe('TenderDigestScheduler — Bindung an forTenant() (260909-laa)', () =>
|
||||
expect(forTenant).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('die Kandidatenabfrage laeuft ueber den System-Klienten: __systemCallLog enthaelt genau tenderMatch.findMany, nichts aus der Schleife (260914-eym)', async () => {
|
||||
vi.mocked(forSystem).mockClear();
|
||||
const users = new Map([['user-1', { id: 'user-1', email: 'a@tenant.de', tenantId: 'tenant-1' }]]);
|
||||
const prefs = new Map([['user-1', { userId: 'user-1', digestInterval: 'daily' }]]);
|
||||
const matches = [makeMatch({ userId: 'user-1', tenantId: 'tenant-1' })];
|
||||
const prisma = makeFakePrisma({ matches, prefs, users });
|
||||
const mail = { sendDigest: vi.fn().mockResolvedValue(true) };
|
||||
const scheduler = new TenderDigestScheduler(makeSchedulerRegistry() as any, prisma as any, mail as any);
|
||||
|
||||
await scheduler.runDigest(TUESDAY);
|
||||
|
||||
expect(forSystem).toHaveBeenCalledTimes(1);
|
||||
expect(forSystem).toHaveBeenCalledWith(prisma);
|
||||
expect(prisma.__systemCallLog).toEqual([{ model: 'tenderMatch', method: 'findMany' }]);
|
||||
// Die Schleife (Praeferenz, Treffer, Benutzer, Stempelung) lief gebunden,
|
||||
// nicht ueber den System-Klienten.
|
||||
expect(prisma.__boundCallLog.length).toBeGreaterThan(0);
|
||||
expect(mail.sendDigest).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('die Zugriffe je Kandidatenzeile binden an den Mandanten DIESER Zeile — Zugriffe innerhalb der Schleife laufen auf dem gebundenen Client', async () => {
|
||||
const users = new Map([['user-1', { id: 'user-1', email: 'a@tenant.de', tenantId: 'tenant-1' }]]);
|
||||
const prefs = new Map([['user-1', { userId: 'user-1', digestInterval: 'daily' }]]);
|
||||
|
||||
Reference in New Issue
Block a user