import { describe, it, expect, beforeEach, afterEach } from 'vitest' import { existsSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs' import { join } from 'node:path' import { tmpdir } from 'node:os' import { TRUSTSTORE_PASSWORD, DEFAULT_COT_PORT, getCotPort, COT_TLS_REQUIRED_MESSAGE, getCotSslPaths, buildP12FromCertPath, } from '../../server/utils/cotSsl.js' import { withTemporaryEnv } from '../helpers/env.js' describe('cotSsl', () => { const testPaths = { testCertDir: null, testCertPath: null, testKeyPath: null, } beforeEach(() => { testPaths.testCertDir = join(tmpdir(), `kestrelos-test-${Date.now()}`) mkdirSync(testPaths.testCertDir, { recursive: true }) testPaths.testCertPath = join(testPaths.testCertDir, 'cert.pem') testPaths.testKeyPath = join(testPaths.testCertDir, 'key.pem') writeFileSync(testPaths.testCertPath, '-----BEGIN CERTIFICATE-----\nTEST\n-----END CERTIFICATE-----\n') writeFileSync(testPaths.testKeyPath, '-----BEGIN PRIVATE KEY-----\nTEST\n-----END PRIVATE KEY-----\n') }) afterEach(() => { try { if (existsSync(testPaths.testCertPath)) unlinkSync(testPaths.testCertPath) if (existsSync(testPaths.testKeyPath)) unlinkSync(testPaths.testKeyPath) } catch { // Ignore cleanup errors } }) describe('constants', () => { it.each([ ['TRUSTSTORE_PASSWORD', TRUSTSTORE_PASSWORD, 'kestrelos'], ['DEFAULT_COT_PORT', DEFAULT_COT_PORT, 8089], ])('exports %s', (name, value, expected) => { expect(value).toBe(expected) }) it('exports COT_TLS_REQUIRED_MESSAGE', () => { expect(COT_TLS_REQUIRED_MESSAGE).toContain('SSL') }) }) describe('getCotPort', () => { it.each([ [{ COT_PORT: undefined }, DEFAULT_COT_PORT], [{ COT_PORT: '9999' }, 9999], [{ COT_PORT: '8080' }, 8080], ])('returns correct port for env: %j', (env, expected) => { withTemporaryEnv(env, () => { expect(getCotPort()).toBe(expected) }) }) }) describe('getCotSslPaths', () => { it('returns paths from env vars when available, otherwise checks default locations', () => { withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => { const result = getCotSslPaths() if (result !== null) { expect(result).toMatchObject({ certPath: expect.any(String), keyPath: expect.any(String), }) } else { expect(result).toBeNull() } }) }) it('returns paths from COT_SSL_CERT and COT_SSL_KEY env vars', () => { withTemporaryEnv({ COT_SSL_CERT: testPaths.testCertPath, COT_SSL_KEY: testPaths.testKeyPath }, () => { expect(getCotSslPaths()).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath }) }) }) it('returns paths from config parameter when env vars not set', () => { withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => { const config = { cotSslCert: testPaths.testCertPath, cotSslKey: testPaths.testKeyPath } expect(getCotSslPaths(config)).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath }) }) }) it('prefers env vars over config parameter', () => { withTemporaryEnv({ COT_SSL_CERT: testPaths.testCertPath, COT_SSL_KEY: testPaths.testKeyPath }, () => { const config = { cotSslCert: '/other/cert.pem', cotSslKey: '/other/key.pem' } expect(getCotSslPaths(config)).toEqual({ certPath: testPaths.testCertPath, keyPath: testPaths.testKeyPath }) }) }) it('returns paths from config even if files do not exist', () => { withTemporaryEnv({ COT_SSL_CERT: undefined, COT_SSL_KEY: undefined }, () => { const result = getCotSslPaths({ cotSslCert: '/nonexistent/cert.pem', cotSslKey: '/nonexistent/key.pem' }) expect(result).toEqual({ certPath: '/nonexistent/cert.pem', keyPath: '/nonexistent/key.pem' }) }) }) }) describe('buildP12FromCertPath', () => { it('throws error when cert file does not exist', () => { expect(() => { buildP12FromCertPath('/nonexistent/cert.pem', 'password') }).toThrow() }) it('throws error when openssl command fails', () => { const invalidCertPath = join(testPaths.testCertDir, 'invalid.pem') writeFileSync(invalidCertPath, 'invalid cert content') expect(() => { buildP12FromCertPath(invalidCertPath, 'password') }).toThrow() }) it('cleans up temp file on error', () => { const invalidCertPath = join(testPaths.testCertDir, 'invalid.pem') writeFileSync(invalidCertPath, 'invalid cert content') try { buildP12FromCertPath(invalidCertPath, 'password') } catch { // Expected to throw } // Function should clean up on error - test passes if no exception during cleanup expect(true).toBe(true) }) }) })