import { describe, expect, it } from "vitest"; import { EventEmitter } from "node:events"; import tls from "node:tls"; import { certificateDaysRemaining } from "./tls"; import { collectTls, type TlsConnector } from "./tls"; describe("TLS certificate date handling", () => { it("returns whole days remaining from a supplied clock", () => { const now = Date.parse("2026-01-01T12:00:00.000Z"); expect(certificateDaysRemaining("Jan 11 12:00:00 2026 GMT", now)).toBe(10); expect(certificateDaysRemaining("Dec 31 12:00:00 2025 GMT", now)).toBe(-1); }); it("does not turn an invalid certificate date into a score deduction", () => { expect(certificateDaysRemaining("not-a-certificate-date", Date.now())).toBeNull(); expect(certificateDaysRemaining(null, Date.now())).toBeNull(); }); it("tries the next validated address after a failed TLS connection", async () => { const attempted: string[] = []; const connect: TlsConnector = ((options) => { const address = String(options.host); attempted.push(address); const socket = new EventEmitter() as EventEmitter & { authorized: boolean; authorizationError: null; getPeerCertificate: () => Record; getProtocol: () => string; destroy: () => void; }; socket.destroy = () => undefined; if (address === "1.1.1.1") { queueMicrotask(() => socket.emit("error", Object.assign(new Error("refused"), { code: "ECONNREFUSED" }))); } else { socket.authorized = true; socket.authorizationError = null; socket.getProtocol = () => "TLSv1.3"; socket.getPeerCertificate = () => ({ subject: { CN: "example.com" }, issuer: { CN: "Example CA" }, subjectaltname: "DNS:example.com", valid_from: "Jan 01 00:00:00 2026 GMT", valid_to: "Jan 01 00:00:00 2027 GMT", fingerprint256: "AA:BB", }); queueMicrotask(() => socket.emit("secureConnect")); } return socket as unknown as ReturnType; }) as TlsConnector; const result = await collectTls("example.com", ["1.1.1.1", "2.2.2.2"], { connect, now: Date.parse("2026-06-01T00:00:00Z") }); expect(result).toMatchObject({ address: "2.2.2.2", protocol: "TLSv1.3", hostnameValid: true, authorized: true }); expect(attempted).toEqual(["1.1.1.1", "2.2.2.2"]); expect(result.attempts).toEqual([{ address: "1.1.1.1", error: "TLS probe failed (ECONNREFUSED)." }]); }); it("reports certificates that are not yet valid", async () => { const connect: TlsConnector = (() => { const socket = new EventEmitter() as EventEmitter & { authorized: boolean; authorizationError: null; getPeerCertificate: () => Record; getProtocol: () => string; destroy: () => void; }; socket.authorized = true; socket.authorizationError = null; socket.getProtocol = () => "TLSv1.3"; socket.getPeerCertificate = () => ({ subjectaltname: "DNS:example.com", valid_from: "Jul 01 00:00:00 2026 GMT", valid_to: "Jan 01 00:00:00 2027 GMT", }); socket.destroy = () => undefined; queueMicrotask(() => socket.emit("secureConnect")); return socket as unknown as ReturnType; }) as TlsConnector; const result = await collectTls("example.com", ["2.2.2.2"], { connect, now: Date.parse("2026-06-01T00:00:00Z") }); expect(result.notYetValid).toBe(true); expect(result.daysRemaining).toBe(214); }); it("marks malformed or inverted certificate validity dates", async () => { const connect: TlsConnector = (() => { const socket = new EventEmitter() as EventEmitter & { authorized: boolean; authorizationError: null; getPeerCertificate: () => Record; getProtocol: () => string; destroy: () => void; }; socket.authorized = true; socket.authorizationError = null; socket.getProtocol = () => "TLSv1.3"; socket.getPeerCertificate = () => ({ subjectaltname: "DNS:example.com", valid_from: "Jan 02 00:00:00 2026 GMT", valid_to: "Jan 01 00:00:00 2026 GMT", }); socket.destroy = () => undefined; queueMicrotask(() => socket.emit("secureConnect")); return socket as unknown as ReturnType; }) as TlsConnector; const result = await collectTls("example.com", ["2.2.2.2"], { connect, now: Date.parse("2026-01-01T00:00:00Z") }); expect(result).toMatchObject({ validityDatesValid: false, daysRemaining: 0 }); }); it("marks an exhausted address set as a failed collector", async () => { const connect: TlsConnector = (() => { const socket = new EventEmitter() as EventEmitter & { destroy: () => void }; socket.destroy = () => undefined; queueMicrotask(() => socket.emit("error", Object.assign(new Error("unreachable"), { code: "ETIMEDOUT" }))); return socket as unknown as ReturnType; }) as TlsConnector; const result = await collectTls("example.com", ["2.2.2.2"], { connect, deadline: Date.now() + 1_000 }); expect(result._meta).toMatchObject({ status: "failed" }); }); });