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:
@@ -8,9 +8,12 @@ import { LdapConfigService } from './ldap-config.service';
|
||||
// Implementierung auf ein zweites, unterscheidbares Client-Objekt um.
|
||||
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
||||
forTenant: vi.fn((p: unknown) => p),
|
||||
// Systemkontext (260914-eym): liefert den in `__systemClient` hinterlegten
|
||||
// Klienten, sonst denselben Client (Bestandstests).
|
||||
forSystem: vi.fn((p: any) => p.__systemClient ?? p),
|
||||
}));
|
||||
|
||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||
import { forSystem, forTenant } from '../prisma/prisma-tenant.extension';
|
||||
|
||||
/**
|
||||
* Das Bind-Passwort ist das einzige Zugangsdatum, das nicht gehasht werden
|
||||
@@ -298,14 +301,65 @@ describe('LdapConfigService — Bindung an forTenant() (260909-ipc)', () => {
|
||||
expect(prisma.ldapFieldMapping.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('getAllActiveConfigs() bleibt bewusst uebergreifend — kein Mandantenkontext', async () => {
|
||||
await service.getAllActiveConfigs();
|
||||
it('getAllActiveConfigs() liest ueber den Systemkontext: forSystem genau einmal, forTenant nie (260914-eym)', async () => {
|
||||
const systemClient = {
|
||||
ldapConfig: { findMany: vi.fn().mockResolvedValue([{ ...CONFIG_ROW }]) },
|
||||
};
|
||||
prisma.__systemClient = systemClient;
|
||||
|
||||
const result = await service.getAllActiveConfigs();
|
||||
|
||||
expect(forSystem).toHaveBeenCalledTimes(1);
|
||||
expect(forSystem).toHaveBeenCalledWith(prisma);
|
||||
expect(forTenant).not.toHaveBeenCalled();
|
||||
expect(systemClient.ldapConfig.findMany).toHaveBeenCalledWith({
|
||||
where: { isActive: true },
|
||||
include: { tenant: true, fieldMappings: true },
|
||||
});
|
||||
expect(prisma.ldapConfig.findMany).not.toHaveBeenCalled();
|
||||
expect(result).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('onApplicationBootstrap() bleibt bewusst uebergreifend — kein Mandantenkontext', async () => {
|
||||
prisma.ldapConfig.findMany.mockResolvedValue([]);
|
||||
it('onApplicationBootstrap() mit leerer Liste: forSystem einmal, forTenant nie, kein Update (Leere ist Nichtstun, 260914-eym)', async () => {
|
||||
const systemClient = { ldapConfig: { findMany: vi.fn().mockResolvedValue([]) } };
|
||||
prisma.__systemClient = systemClient;
|
||||
|
||||
await service.onApplicationBootstrap();
|
||||
|
||||
expect(forSystem).toHaveBeenCalledTimes(1);
|
||||
expect(forTenant).not.toHaveBeenCalled();
|
||||
expect(prisma.ldapConfig.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('onApplicationBootstrap() mit einer Altzeile (Klartext, t1): liest system, schreibt GEBUNDEN — forTenant genau einmal mit t1, update traegt das verschluesselte Kennwort (260914-eym)', async () => {
|
||||
const systemClient = {
|
||||
ldapConfig: {
|
||||
findMany: vi.fn().mockResolvedValue([
|
||||
{ id: 'alt', tenantId: 't1', encryptedBindPassword: 'klartext' },
|
||||
]),
|
||||
},
|
||||
};
|
||||
prisma.__systemClient = systemClient;
|
||||
const boundClient = {
|
||||
ldapConfig: { update: vi.fn((args: any) => Promise.resolve({ ...CONFIG_ROW, ...args.data })) },
|
||||
};
|
||||
vi.mocked(forTenant).mockImplementation(() => boundClient as any);
|
||||
|
||||
await service.onApplicationBootstrap();
|
||||
|
||||
expect(forSystem).toHaveBeenCalledTimes(1);
|
||||
expect(forTenant).toHaveBeenCalledTimes(1);
|
||||
expect(forTenant).toHaveBeenCalledWith(prisma, 't1');
|
||||
expect(boundClient.ldapConfig.update).toHaveBeenCalledTimes(1);
|
||||
const call = boundClient.ldapConfig.update.mock.calls[0][0];
|
||||
expect(call.where).toEqual({ id: 'alt' });
|
||||
expect(call.data.encryptedBindPassword).toBe(
|
||||
'aa11:bb22:' + Buffer.from('klartext').toString('hex'),
|
||||
);
|
||||
// Der rohe Client schreibt NICHT.
|
||||
expect(prisma.ldapConfig.update).not.toHaveBeenCalled();
|
||||
|
||||
// Implementierung zuruecksetzen (vi.clearAllMocks loescht nur Aufrufe).
|
||||
vi.mocked(forTenant).mockImplementation((p: unknown) => p as any);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user