From f22404957b990b31d034de9d739ebe5bd48b1e32 Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 25 Oct 2025 21:35:04 -0400 Subject: [PATCH] test: add comprehensive coverage for encryption-utils and auth-utils Added missing test coverage from code review feedback: - encryption-utils.test.ts: * Added error handling tests for encryptApiKey fallback to plaintext * Added error handling tests for decryptApiKey throwing on failure * Added tests for isEncryptionAvailable function * Achieved 100% coverage on all metrics - auth-utils.test.ts (new file): * Added comprehensive tests for generateApiKey function * Added validation tests for validateApiKey function * Tests edge cases: empty keys, short keys, null/undefined * Achieved 100% coverage on all metrics All tests pass (569 tests). Overall coverage improved: - auth-utils.ts: 100% statements, 100% branches, 100% functions - encryption-utils.ts: 100% statements, 100% branches, 100% functions --- tests/auth-utils.test.ts | 103 +++++++++++++++++++++++++++++++++ tests/encryption-utils.test.ts | 49 +++++++++++++++- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 tests/auth-utils.test.ts diff --git a/tests/auth-utils.test.ts b/tests/auth-utils.test.ts new file mode 100644 index 0000000..39705ac --- /dev/null +++ b/tests/auth-utils.test.ts @@ -0,0 +1,103 @@ +import { generateApiKey, validateApiKey } from '../src/utils/auth-utils'; + +describe('Auth Utils', () => { + describe('generateApiKey', () => { + it('should generate API key with default length of 32 characters', () => { + const apiKey = generateApiKey(); + expect(apiKey).toHaveLength(32); + }); + + it('should generate API key with custom length', () => { + const length = 64; + const apiKey = generateApiKey(length); + expect(apiKey).toHaveLength(length); + }); + + it('should generate different keys on each call', () => { + const key1 = generateApiKey(); + const key2 = generateApiKey(); + expect(key1).not.toBe(key2); + }); + + it('should only use valid charset characters', () => { + const apiKey = generateApiKey(100); + const validChars = /^[A-Za-z0-9_-]+$/; + expect(apiKey).toMatch(validChars); + }); + + it('should generate key of length 1', () => { + const apiKey = generateApiKey(1); + expect(apiKey).toHaveLength(1); + }); + + it('should generate very long keys', () => { + const apiKey = generateApiKey(256); + expect(apiKey).toHaveLength(256); + }); + + it('should use cryptographically secure random values', () => { + // Generate many keys and check for reasonable distribution + const keys = new Set(); + for (let i = 0; i < 100; i++) { + keys.add(generateApiKey(8)); + } + // With 8 chars from a 64-char set, we should get unique values + expect(keys.size).toBeGreaterThan(95); // Allow for small collision probability + }); + }); + + describe('validateApiKey', () => { + it('should validate a strong API key', () => { + const result = validateApiKey('this-is-a-strong-key-123'); + expect(result.isValid).toBe(true); + expect(result.error).toBeUndefined(); + }); + + it('should reject empty API key', () => { + const result = validateApiKey(''); + expect(result.isValid).toBe(false); + expect(result.error).toBe('API key cannot be empty'); + }); + + it('should reject whitespace-only API key', () => { + const result = validateApiKey(' '); + expect(result.isValid).toBe(false); + expect(result.error).toBe('API key cannot be empty'); + }); + + it('should reject API key shorter than 16 characters', () => { + const result = validateApiKey('short'); + expect(result.isValid).toBe(false); + expect(result.error).toBe('API key must be at least 16 characters long'); + }); + + it('should accept API key exactly 16 characters', () => { + const result = validateApiKey('1234567890123456'); + expect(result.isValid).toBe(true); + expect(result.error).toBeUndefined(); + }); + + it('should accept API key longer than 16 characters', () => { + const result = validateApiKey('12345678901234567890'); + expect(result.isValid).toBe(true); + expect(result.error).toBeUndefined(); + }); + + it('should reject null or undefined API key', () => { + const result1 = validateApiKey(null as any); + expect(result1.isValid).toBe(false); + expect(result1.error).toBe('API key cannot be empty'); + + const result2 = validateApiKey(undefined as any); + expect(result2.isValid).toBe(false); + expect(result2.error).toBe('API key cannot be empty'); + }); + + it('should validate generated API keys', () => { + const apiKey = generateApiKey(); + const result = validateApiKey(apiKey); + expect(result.isValid).toBe(true); + expect(result.error).toBeUndefined(); + }); + }); +}); diff --git a/tests/encryption-utils.test.ts b/tests/encryption-utils.test.ts index fa6bf96..3c670f8 100644 --- a/tests/encryption-utils.test.ts +++ b/tests/encryption-utils.test.ts @@ -1,4 +1,4 @@ -import { encryptApiKey, decryptApiKey } from '../src/utils/encryption-utils'; +import { encryptApiKey, decryptApiKey, isEncryptionAvailable } from '../src/utils/encryption-utils'; // Mock electron module jest.mock('electron', () => ({ @@ -70,4 +70,51 @@ describe('Encryption Utils', () => { expect(encrypted).not.toBe(original); }); }); + + describe('error handling', () => { + it('should handle encryption errors and fallback to plaintext', () => { + const { safeStorage } = require('electron'); + const originalEncrypt = safeStorage.encryptString; + safeStorage.encryptString = jest.fn(() => { + throw new Error('Encryption failed'); + }); + + const apiKey = 'test-api-key-12345'; + const result = encryptApiKey(apiKey); + + expect(result).toBe(apiKey); // Should return plaintext on error + safeStorage.encryptString = originalEncrypt; // Restore + }); + + it('should throw error when decryption fails', () => { + const { safeStorage } = require('electron'); + const originalDecrypt = safeStorage.decryptString; + safeStorage.decryptString = jest.fn(() => { + throw new Error('Decryption failed'); + }); + + const encrypted = 'encrypted:aW52YWxpZA=='; // Invalid encrypted data + + expect(() => decryptApiKey(encrypted)).toThrow('Failed to decrypt API key'); + safeStorage.decryptString = originalDecrypt; // Restore + }); + }); + + describe('isEncryptionAvailable', () => { + it('should return true when encryption is available', () => { + const { isEncryptionAvailable } = require('../src/utils/encryption-utils'); + const { safeStorage } = require('electron'); + + safeStorage.isEncryptionAvailable.mockReturnValueOnce(true); + expect(isEncryptionAvailable()).toBe(true); + }); + + it('should return false when encryption is not available', () => { + const { isEncryptionAvailable } = require('../src/utils/encryption-utils'); + const { safeStorage } = require('electron'); + + safeStorage.isEncryptionAvailable.mockReturnValueOnce(false); + expect(isEncryptionAvailable()).toBe(false); + }); + }); });