/** * Pruebas de resolveNodeSqlPassword: la columna database_nodes.sql_password se autorrellena * al asignar un restaurador copiando su sobre cifrado (gcm:), así que al resolverla hay que * descifrarla. Cubre: sin valor → global, texto plano legado, sobre gcm: descifrado, y * descifrado que falla → global (no debe tumbar el dashboard). */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { env } from '$env/dynamic/private'; // Se conserva el isEncrypted real (trivial: prefijo gcm:) y solo se controla el descifrado. vi.mock('./crypto', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, decryptSecret: vi.fn() }; }); import { decryptSecret } from './crypto'; import { resolveNodeSqlPassword, parseMssqlServer } from './mssql-nodes'; const decryptMock = vi.mocked(decryptSecret); beforeEach(() => { vi.clearAllMocks(); env.PANEL_MSSQL_PASSWORD = 'GLOBAL_PWD'; }); describe('resolveNodeSqlPassword', () => { it('sin valor (null/undefined/vacío/espacios) usa la contraseña global', () => { expect(resolveNodeSqlPassword(null)).toBe('GLOBAL_PWD'); expect(resolveNodeSqlPassword(undefined)).toBe('GLOBAL_PWD'); expect(resolveNodeSqlPassword('')).toBe('GLOBAL_PWD'); expect(resolveNodeSqlPassword(' ')).toBe('GLOBAL_PWD'); expect(decryptMock).not.toHaveBeenCalled(); }); it('texto plano legado (sin prefijo gcm:) se usa tal cual y se recorta, sin descifrar', () => { expect(resolveNodeSqlPassword(' MiPassPlano ')).toBe('MiPassPlano'); expect(decryptMock).not.toHaveBeenCalled(); }); it('sobre cifrado gcm: se descifra y devuelve el texto plano', () => { decryptMock.mockReturnValue('SecretoDescifrado'); const envelope = 'gcm:aXY=:dGFn:Y2lwaGVy'; expect(resolveNodeSqlPassword(envelope)).toBe('SecretoDescifrado'); expect(decryptMock).toHaveBeenCalledWith(envelope); }); it('si el descifrado falla (sobre corrupto o clave equivocada) cae a la global sin propagar el error', () => { decryptMock.mockImplementation(() => { throw new Error('autenticación GCM inválida'); }); expect(resolveNodeSqlPassword('gcm:corrupto')).toBe('GLOBAL_PWD'); expect(decryptMock).toHaveBeenCalledOnce(); }); }); describe('parseMssqlServer', () => { it('host,puerto → server y port separados (evita el bug host,puerto:1433)', () => { expect(parseMssqlServer('192.168.1.20,1433')).toEqual({ server: '192.168.1.20', port: 1433 }); }); it('solo host → sin puerto (tedious usa 1433 por defecto)', () => { expect(parseMssqlServer('SQLSERVER01')).toEqual({ server: 'SQLSERVER01' }); }); it('host\\instancia → instanceName', () => { expect(parseMssqlServer('HOST\\SQLEXPRESS')).toEqual({ server: 'HOST', instanceName: 'SQLEXPRESS' }); }); it('host\\instancia,puerto → server, instanceName y port', () => { expect(parseMssqlServer('HOST\\SQLEXPRESS,1450')).toEqual({ server: 'HOST', port: 1450, instanceName: 'SQLEXPRESS' }); }); it('recorta espacios e ignora puerto no numérico', () => { expect(parseMssqlServer(' 10.0.0.5 , abc ')).toEqual({ server: '10.0.0.5' }); }); });