feat(260923-dhh): Proxmox-Modul Aufgabe 5 - Einstellungsseite (anlegen, bearbeiten, loeschen, testen)
- proxmox.service.ts: updateServer (Muster LdapConfigService.updateConfig - nicht gesendet laesst unveraendert, leer loescht, gefuellt verschluesselt neu; PMG+Token auch beim Bearbeiten abgelehnt, geprueft gegen den EFFEKTIVEN Stand nach Zusammenfuehren), deleteServer - proxmox.controller.ts: PUT/DELETE servers/:id, beide zusaetzlich mit scheduler.refreshTenant() nach dem Schreiben - Frontend: proxmox-api.ts (updateServer/deleteServer/testServer), settings/page.tsx (Rollenpruefung nur Anzeige, Serverliste, Loeschen mit Rueckfrage), ServerForm.tsx (PMG bietet Token gar nicht an, Geheimnisfelder nie vorbefuellt, Zertifikatspruefung-Schalter Standard "pruefen", Verbindungstest mit Klartext-Fehlertext) - umlaut-dictionary.ts: zwei neue, bereits korrekte Woerter (bewusst/gemessene) auf die Positivliste des Regressions-Waechters Tore: api 1311/1311 (>=1240), web 701/701 (>=693), type-check 4/4, lint 5/5, Biome apps/web 53 Warnungen (unveraendert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,7 @@ vi.mock('../prisma/prisma-tenant.extension', () => ({
|
||||
import { Agent } from 'undici';
|
||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||
import { ProxmoxService } from './proxmox.service';
|
||||
import type { CreateProxmoxServerDto } from './dto/proxmox-server.dto';
|
||||
import type { CreateProxmoxServerDto, UpdateProxmoxServerDto } from './dto/proxmox-server.dto';
|
||||
|
||||
/** Durchschaubarer Ersatz fuer AES-256-GCM — Zusammenspiel unter Test, nicht die Bibliothek. */
|
||||
const crypto = {
|
||||
@@ -81,6 +81,28 @@ function makeFakePrisma() {
|
||||
return { ...row };
|
||||
},
|
||||
),
|
||||
update: vi.fn(
|
||||
async ({
|
||||
where,
|
||||
data,
|
||||
select,
|
||||
}: {
|
||||
where: { id: string };
|
||||
data: Record<string, unknown>;
|
||||
select?: any;
|
||||
}) => {
|
||||
const existing = servers.get(where.id);
|
||||
const updated = { ...existing, ...data, updatedAt: new Date() };
|
||||
servers.set(where.id, updated);
|
||||
return applySelect(updated, select);
|
||||
},
|
||||
),
|
||||
delete: vi.fn(async ({ where }: { where: { id: string } }) => {
|
||||
const row = servers.get(where.id);
|
||||
servers.delete(where.id);
|
||||
statuses.delete(where.id); // Fremdschluessel mit Loeschweitergabe (onDelete: Cascade)
|
||||
return row ? { ...row } : null;
|
||||
}),
|
||||
};
|
||||
|
||||
const proxmoxServerStatus = {
|
||||
@@ -434,3 +456,71 @@ describe('ProxmoxService — Aufgabe 4 (Verbindungstest, Zehn-Sekunden-Sperre)',
|
||||
expect(servers[0]).toMatchObject({ tenantId: 'tenant-a', pollIntervalMin: 5 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProxmoxService — Aufgabe 5 (Bearbeiten, Loeschen)', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('ein NICHT gesendetes Geheimnisfeld laesst den gespeicherten Wert unveraendert', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const service = new ProxmoxService(prisma as any, crypto as any);
|
||||
const created = await service.createServer('tenant-a', TOKEN_DTO);
|
||||
const storedBefore = prisma.__servers.get((created as any).id).encryptedTokenSecret;
|
||||
|
||||
await service.updateServer('tenant-a', (created as any).id, { name: 'neuer-name' });
|
||||
|
||||
expect(prisma.__servers.get((created as any).id).encryptedTokenSecret).toBe(storedBefore);
|
||||
expect(prisma.__servers.get((created as any).id).name).toBe('neuer-name');
|
||||
});
|
||||
|
||||
it('eine LEERE Zeichenkette loescht das Geheimnis, ein gefuellter Wert verschluesselt neu', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const service = new ProxmoxService(prisma as any, crypto as any);
|
||||
const created = await service.createServer('tenant-a', TOKEN_DTO);
|
||||
|
||||
await service.updateServer('tenant-a', (created as any).id, { tokenSecret: '' });
|
||||
expect(prisma.__servers.get((created as any).id).encryptedTokenSecret).toBeNull();
|
||||
|
||||
await service.updateServer('tenant-a', (created as any).id, { tokenSecret: 'neues-geheimnis' });
|
||||
const stored = prisma.__servers.get((created as any).id).encryptedTokenSecret;
|
||||
expect(stored).not.toBe('neues-geheimnis');
|
||||
expect(stored).toMatch(/^[0-9a-f]+:[0-9a-f]+:[0-9a-f]*$/i);
|
||||
});
|
||||
|
||||
it('PMG plus Token wird auch beim Bearbeiten abgelehnt — auch wenn nur authMethod gesendet wird', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const service = new ProxmoxService(prisma as any, crypto as any);
|
||||
const created = await service.createServer('tenant-a', {
|
||||
name: 'pmg-1',
|
||||
productType: 'pmg',
|
||||
baseUrl: 'https://pmg.intern',
|
||||
authMethod: 'password',
|
||||
username: 'admin@pmg',
|
||||
password: 'geheim',
|
||||
});
|
||||
|
||||
const dto: UpdateProxmoxServerDto = { authMethod: 'token', tokenId: 'x', tokenSecret: 'y' };
|
||||
await expect(service.updateServer('tenant-a', (created as any).id, dto)).rejects.toThrow();
|
||||
});
|
||||
|
||||
it('loescht einen Server samt Zwischenlagerzeile', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const service = new ProxmoxService(prisma as any, crypto as any);
|
||||
const created = await service.createServer('tenant-a', TOKEN_DTO);
|
||||
expect(prisma.__statuses.has((created as any).id)).toBe(true);
|
||||
|
||||
const deleted = await service.deleteServer('tenant-a', (created as any).id);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
expect(prisma.__servers.has((created as any).id)).toBe(false);
|
||||
expect(prisma.__statuses.has((created as any).id)).toBe(false);
|
||||
});
|
||||
|
||||
it('deleteServer liefert false fuer einen unbekannten Server', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
const service = new ProxmoxService(prisma as any, crypto as any);
|
||||
expect(await service.deleteServer('tenant-a', 'unbekannt')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user