test(260911-gwh): RED — neue Testdateien fuer favorites/settings mit dem Zwei-Klienten-Nachbau
- favorites.service.spec.ts (NEU, 23 Faelle): ungebundener Nachbau hat KEIN favoriteLink/widgetInstance-Modell; erwartet die Zielsignatur list/create/update/remove/getIconBytes(tenantId, ...) und den Widget-Besitzriegel in create -- scheitert erwartungsgemaess an "Cannot read properties of undefined" gegen den heutigen Dienst (22/23 rot) - settings.service.spec.ts (NEU, 20 Faelle): ungebundener Nachbau bietet fuer smtpConfig NUR findFirst, gebundener Klient NUR findUnique/upsert; erwartet loadAnySmtpConfigForStartupTransport() (Startpfad-Umbenennung) und den Null-Klienten-Nachweis fuer den Startpfad -- 18/20 rot Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import { SettingsService } from './settings.service';
|
||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||
|
||||
/**
|
||||
* SettingsService.spec — NEU (260911-gwh). Der Bereich `settings` hatte VOR
|
||||
* diesem Lauf KEINE Testdatei (Befund J). Zwei-Klienten-Nachbau, aber mit
|
||||
* einer GRENZE als Bauform (anders als `favorites`): der UNGEBUNDENE Nachbau
|
||||
* bietet fuer `smtpConfig` AUSSCHLIESSLICH `findFirst` (der Startpfad) —
|
||||
* KEIN `findUnique`, KEIN `upsert`; der GEBUNDENE Klient bietet
|
||||
* AUSSCHLIESSLICH `findUnique`/`upsert` — KEIN `findFirst`. Ein gebundener
|
||||
* Startpfad scheitert damit ebenso hart wie ein ungebundener Anfrageweg
|
||||
* ("X is not a function" statt eines stillen Fallbacks).
|
||||
*
|
||||
* `nodemailer` wird per `vi.mock` ersetzt — kein echter Transport (lokal
|
||||
* gibt es keinen `mailhog`).
|
||||
*/
|
||||
vi.mock('../prisma/prisma-tenant.extension', () => ({
|
||||
forTenant: vi.fn((unboundClient: any, tenantId: string) => unboundClient.__makeBoundClient(tenantId)),
|
||||
}));
|
||||
|
||||
let mockVerify = vi.fn(async () => true);
|
||||
let mockSendMail = vi.fn(async () => ({}));
|
||||
vi.mock('nodemailer', () => ({
|
||||
createTransport: vi.fn(() => ({
|
||||
verify: (...args: unknown[]) => (mockVerify as any)(...args),
|
||||
sendMail: (...args: unknown[]) => (mockSendMail as any)(...args),
|
||||
})),
|
||||
}));
|
||||
|
||||
interface FakeSmtpRow {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
host: string;
|
||||
port: number;
|
||||
encryption: string;
|
||||
username: string | null;
|
||||
encryptedPassword: string | null;
|
||||
fromAddress: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
}
|
||||
|
||||
interface BoundCall {
|
||||
tenantId: string;
|
||||
model: 'smtpConfig';
|
||||
method: string;
|
||||
}
|
||||
|
||||
function applySelect(row: Record<string, unknown>, select?: Record<string, boolean>) {
|
||||
if (!select) return { ...row };
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const key of Object.keys(select)) {
|
||||
if (select[key]) out[key] = row[key];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function makeFakePrisma(rows: FakeSmtpRow[] = []) {
|
||||
const configs = new Map(rows.map((r) => [r.tenantId, { ...r }]));
|
||||
const boundCallLog: BoundCall[] = [];
|
||||
let autoId = rows.length;
|
||||
|
||||
const unboundSmtpConfig = {
|
||||
findFirst: vi.fn(async () => {
|
||||
const first = configs.values().next().value;
|
||||
return first ? { ...first } : null;
|
||||
}),
|
||||
};
|
||||
|
||||
function makeScopedSmtpConfig(tenantId: string) {
|
||||
return {
|
||||
findUnique: async ({ where, select }: any) => {
|
||||
boundCallLog.push({ tenantId, model: 'smtpConfig', method: 'findUnique' });
|
||||
const row = configs.get(where.tenantId);
|
||||
if (!row || row.tenantId !== tenantId) return null;
|
||||
return applySelect(row, select);
|
||||
},
|
||||
upsert: async ({ where, create, update, select }: any) => {
|
||||
boundCallLog.push({ tenantId, model: 'smtpConfig', method: 'upsert' });
|
||||
if (where.tenantId !== tenantId) return null;
|
||||
const existing = configs.get(tenantId);
|
||||
const record = existing
|
||||
? { ...existing, ...update }
|
||||
: { id: `smtp-${++autoId}`, createdAt: new Date(), ...create };
|
||||
record.updatedAt = new Date();
|
||||
configs.set(tenantId, record);
|
||||
return applySelect(record, select);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const fake: any = {
|
||||
smtpConfig: unboundSmtpConfig,
|
||||
__configs: configs,
|
||||
__boundCallLog: boundCallLog,
|
||||
__makeBoundClient(tenantId: string) {
|
||||
return { smtpConfig: makeScopedSmtpConfig(tenantId) };
|
||||
},
|
||||
};
|
||||
return fake;
|
||||
}
|
||||
|
||||
function expectBoundCall(prisma: any, tenantId: string, method: string) {
|
||||
const found = prisma.__boundCallLog.some(
|
||||
(c: BoundCall) => c.tenantId === tenantId && c.model === 'smtpConfig' && c.method === method,
|
||||
);
|
||||
expect(
|
||||
found,
|
||||
`erwarteter gebundener Aufruf smtpConfig.${method}(tenant=${tenantId}) fehlt im Protokoll: ${JSON.stringify(prisma.__boundCallLog)}`,
|
||||
).toBe(true);
|
||||
}
|
||||
|
||||
function makeFakeCrypto() {
|
||||
return {
|
||||
encrypt: vi.fn((s: string) => `enc(${s})`),
|
||||
decrypt: vi.fn((s: string) => s.slice(4, -1)),
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockVerify = vi.fn(async () => true);
|
||||
mockSendMail = vi.fn(async () => ({}));
|
||||
});
|
||||
|
||||
describe('SettingsService — Bindung an forTenant() (260911-gwh)', () => {
|
||||
it('scheitert an "X is not a function", wenn getSmtpConfig versehentlich ungebunden auf dem Basisclient laeuft (Falsifizierungsform)', () => {
|
||||
const prisma = makeFakePrisma();
|
||||
expect((prisma.smtpConfig as any).findUnique).toBeUndefined();
|
||||
expect((prisma.smtpConfig as any).upsert).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('getSmtpConfig', () => {
|
||||
it('liefert die Zeile mit encryptedPassword (fuer hasPassword), ohne Felder ausserhalb des select', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.getSmtpConfig('t1');
|
||||
|
||||
expect(result?.encryptedPassword).toBe('enc(geheim)');
|
||||
expect(result?.host).toBe('smtp-a.example.invalid');
|
||||
expectBoundCall(prisma, 't1', 'findUnique');
|
||||
});
|
||||
|
||||
it('fremder Mandant: null', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: null,
|
||||
encryptedPassword: null,
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
const result = await service.getSmtpConfig('t2');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveSmtpConfig', () => {
|
||||
it('mit Kennwort: encrypt wird GENAU mit dem Klartext aufgerufen, upsert traegt encryptedPassword, Rueckgabe OHNE encryptedPassword', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.saveSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
password: 'klartext-geheim',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(crypto.encrypt).toHaveBeenCalledWith('klartext-geheim');
|
||||
expect((result as any).encryptedPassword).toBeUndefined();
|
||||
const stored = prisma.__configs.get('t1');
|
||||
expect(stored.encryptedPassword).toBe('enc(klartext-geheim)');
|
||||
expectBoundCall(prisma, 't1', 'upsert');
|
||||
const logged = JSON.stringify(prisma.__boundCallLog);
|
||||
expect(logged).not.toContain('klartext-geheim');
|
||||
});
|
||||
|
||||
it('ohne Kennwort (leer oder fehlend): encrypt NICHT aufgerufen, update traegt KEINEN Schluessel encryptedPassword, username fehlend -> null', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'alt.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'alt-user',
|
||||
encryptedPassword: 'enc(alt-geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
await service.saveSmtpConfig('t1', {
|
||||
host: 'neu.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
password: '',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(crypto.encrypt).not.toHaveBeenCalled();
|
||||
const stored = prisma.__configs.get('t1');
|
||||
expect(stored.encryptedPassword).toBe('enc(alt-geheim)'); // bestehendes bleibt
|
||||
expect(stored.username).toBeNull();
|
||||
});
|
||||
|
||||
it('unter t2, wenn nur t1 eine Zeile hat: legt fuer t2 an, t1 bleibt unveraendert (Semantik nach der Bindung)', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: null,
|
||||
encryptedPassword: null,
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
await service.saveSmtpConfig('t2', {
|
||||
host: 'b.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'b@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(prisma.__configs.get('t1').host).toBe('a.example.invalid');
|
||||
expect(prisma.__configs.get('t2').host).toBe('b.example.invalid');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDecryptedSmtpConfig', () => {
|
||||
it('entschluesselt encryptedPassword, liefert die Form die tender-mail.service.ts erwartet', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.getDecryptedSmtpConfig('t1');
|
||||
|
||||
expect(crypto.decrypt).toHaveBeenCalledWith('enc(geheim)');
|
||||
expect(result).toEqual({
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
fromAddress: 'a@example.invalid',
|
||||
decryptedPassword: 'geheim',
|
||||
});
|
||||
});
|
||||
|
||||
it('ohne encryptedPassword: decryptedPassword null, decrypt NICHT aufgerufen', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: null,
|
||||
encryptedPassword: null,
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.getDecryptedSmtpConfig('t1');
|
||||
|
||||
expect(result?.decryptedPassword).toBeNull();
|
||||
expect(crypto.decrypt).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('T-GWH-01: fremder Mandant -> null, decrypt NICHT aufgerufen', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.getDecryptedSmtpConfig('t2');
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(crypto.decrypt).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('testSmtpConfig', () => {
|
||||
const storedRow: FakeSmtpRow = {
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
};
|
||||
|
||||
it('ohne Kennwort/Benutzername im DTO: greift auf die gespeicherten Werte zurueck, ruft verify auf, { success: true }', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
const result = await service.testSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(result).toEqual({ success: true });
|
||||
expect(mockVerify).toHaveBeenCalled();
|
||||
expect(mockSendMail).not.toHaveBeenCalled();
|
||||
expect(nodemailer.createTransport).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ auth: { user: 'user-a', pass: 'geheim' } }),
|
||||
);
|
||||
});
|
||||
|
||||
it('mit testTo: sendMail statt verify, from/to aus dto', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
await service.testSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'a@example.invalid',
|
||||
testTo: 'ziel@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(mockSendMail).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ from: 'a@example.invalid', to: 'ziel@example.invalid' }),
|
||||
);
|
||||
expect(mockVerify).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('verify wirft -> { success: false }, kein Wurf nach aussen', async () => {
|
||||
mockVerify = vi.fn(async () => {
|
||||
throw new Error('ECONNREFUSED');
|
||||
});
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
const result = await service.testSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it('fremder Mandant ohne Kennwort im DTO: auth ohne Benutzer, kein Fehler', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
await service.testSmtpConfig('t2', {
|
||||
host: 'x.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'x@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(nodemailer.createTransport).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ auth: undefined }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadAnySmtpConfigForStartupTransport (Startpfad, bewusst ungebunden)', () => {
|
||||
it('laeuft ueber den UNGEBUNDENEN Nachbau (findFirst), liefert secure/requireTLS/entschluesseltes Kennwort', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 465,
|
||||
encryption: 'ssl-tls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const crypto = makeFakeCrypto();
|
||||
const service = new SettingsService(prisma as any, crypto as any);
|
||||
|
||||
const result = await service.loadAnySmtpConfigForStartupTransport();
|
||||
|
||||
expect(result).toEqual({
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 465,
|
||||
secure: true,
|
||||
requireTLS: false,
|
||||
username: 'user-a',
|
||||
password: 'geheim',
|
||||
fromAddress: 'a@example.invalid',
|
||||
});
|
||||
});
|
||||
|
||||
it('requireTLS bei starttls', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: null,
|
||||
encryptedPassword: null,
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
const result = await service.loadAnySmtpConfigForStartupTransport();
|
||||
|
||||
expect(result?.secure).toBe(false);
|
||||
expect(result?.requireTLS).toBe(true);
|
||||
});
|
||||
|
||||
it('leerer Nachbau -> null', async () => {
|
||||
const prisma = makeFakePrisma([]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
const result = await service.loadAnySmtpConfigForStartupTransport();
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('Null-Klienten-Nachweis: der Startpfad erzeugt KEINEN gebundenen Klienten (gemessen, nicht behauptet)', async () => {
|
||||
const prisma = makeFakePrisma([
|
||||
{
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: null,
|
||||
encryptedPassword: null,
|
||||
fromAddress: 'a@example.invalid',
|
||||
},
|
||||
]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
vi.mocked(forTenant).mockClear();
|
||||
await service.loadAnySmtpConfigForStartupTransport();
|
||||
|
||||
expect(vi.mocked(forTenant).mock.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Wachhund je Anfrageweg', () => {
|
||||
const storedRow: FakeSmtpRow = {
|
||||
id: 'smtp-a',
|
||||
tenantId: 't1',
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'user-a',
|
||||
encryptedPassword: 'enc(geheim)',
|
||||
fromAddress: 'a@example.invalid',
|
||||
};
|
||||
|
||||
it('genau EIN gebundener Klient je Aufruf von getSmtpConfig/saveSmtpConfig/getDecryptedSmtpConfig', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
for (const call of [
|
||||
() => service.getSmtpConfig('t1'),
|
||||
() =>
|
||||
service.saveSmtpConfig('t1', {
|
||||
host: 'x.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'x@invalid.de',
|
||||
} as any),
|
||||
() => service.getDecryptedSmtpConfig('t1'),
|
||||
]) {
|
||||
vi.mocked(forTenant).mockClear();
|
||||
await call();
|
||||
expect(vi.mocked(forTenant).mock.calls.length).toBe(1);
|
||||
}
|
||||
});
|
||||
|
||||
it('testSmtpConfig erzeugt genau einen gebundenen Klienten, wenn es auf gespeicherte Zugangsdaten zurueckgreift', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
vi.mocked(forTenant).mockClear();
|
||||
await service.testSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(vi.mocked(forTenant).mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('testSmtpConfig erzeugt KEINEN gebundenen Klienten, wenn Kennwort und Benutzername im DTO stehen', async () => {
|
||||
const prisma = makeFakePrisma([storedRow]);
|
||||
const service = new SettingsService(prisma as any, makeFakeCrypto() as any);
|
||||
|
||||
vi.mocked(forTenant).mockClear();
|
||||
await service.testSmtpConfig('t1', {
|
||||
host: 'smtp-a.example.invalid',
|
||||
port: 587,
|
||||
encryption: 'starttls',
|
||||
username: 'anderer-user',
|
||||
password: 'anderes-kennwort',
|
||||
fromAddress: 'a@example.invalid',
|
||||
} as any);
|
||||
|
||||
expect(vi.mocked(forTenant).mock.calls.length).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user