| | import path from 'path'; |
| | import axios from 'axios'; |
| | import { readFileAsString } from './files'; |
| | import { loadServiceKey } from './key'; |
| |
|
| | jest.mock('fs'); |
| | jest.mock('axios'); |
| | jest.mock('@librechat/data-schemas', () => ({ |
| | logger: { |
| | error: jest.fn(), |
| | }, |
| | })); |
| |
|
| | jest.mock('./files', () => ({ |
| | readFileAsString: jest.fn(), |
| | })); |
| |
|
| | describe('loadServiceKey', () => { |
| | const mockServiceKey = { |
| | type: 'service_account', |
| | project_id: 'test-project', |
| | private_key_id: 'test-key-id', |
| | private_key: '-----BEGIN PRIVATE KEY-----\ntest-key\n-----END PRIVATE KEY-----', |
| | client_email: 'test@test-project.iam.gserviceaccount.com', |
| | client_id: '123456789', |
| | auth_uri: 'https://accounts.google.com/o/oauth2/auth', |
| | token_uri: 'https://oauth2.googleapis.com/token', |
| | auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs', |
| | client_x509_cert_url: |
| | 'https://www.googleapis.com/robot/v1/metadata/x509/test%40test-project.iam.gserviceaccount.com', |
| | }; |
| |
|
| | beforeEach(() => { |
| | jest.clearAllMocks(); |
| | }); |
| |
|
| | it('should return null if keyPath is empty', async () => { |
| | const result = await loadServiceKey(''); |
| | expect(result).toBeNull(); |
| | }); |
| |
|
| | it('should parse stringified JSON directly', async () => { |
| | const jsonString = JSON.stringify(mockServiceKey); |
| | const result = await loadServiceKey(jsonString); |
| | expect(result).toEqual(mockServiceKey); |
| | }); |
| |
|
| | it('should parse stringified JSON with leading/trailing whitespace', async () => { |
| | const jsonString = ` ${JSON.stringify(mockServiceKey)} `; |
| | const result = await loadServiceKey(jsonString); |
| | expect(result).toEqual(mockServiceKey); |
| | }); |
| |
|
| | it('should load from file path', async () => { |
| | const filePath = '/path/to/service-key.json'; |
| | (readFileAsString as jest.Mock).mockResolvedValue({ |
| | content: JSON.stringify(mockServiceKey), |
| | bytes: JSON.stringify(mockServiceKey).length, |
| | }); |
| |
|
| | const result = await loadServiceKey(filePath); |
| | expect(readFileAsString).toHaveBeenCalledWith(path.resolve(filePath)); |
| | expect(result).toEqual(mockServiceKey); |
| | }); |
| |
|
| | it('should load from URL', async () => { |
| | const url = 'https://example.com/service-key.json'; |
| | (axios.get as jest.Mock).mockResolvedValue({ data: mockServiceKey }); |
| |
|
| | const result = await loadServiceKey(url); |
| | expect(axios.get).toHaveBeenCalledWith(url); |
| | expect(result).toEqual(mockServiceKey); |
| | }); |
| |
|
| | it('should handle invalid JSON string', async () => { |
| | const invalidJson = '{ invalid json }'; |
| | const result = await loadServiceKey(invalidJson); |
| | expect(result).toBeNull(); |
| | }); |
| |
|
| | it('should handle file read errors', async () => { |
| | const filePath = '/path/to/nonexistent.json'; |
| | (readFileAsString as jest.Mock).mockRejectedValue(new Error('File not found')); |
| |
|
| | const result = await loadServiceKey(filePath); |
| | expect(result).toBeNull(); |
| | }); |
| |
|
| | it('should handle URL fetch errors', async () => { |
| | const url = 'https://example.com/service-key.json'; |
| | (axios.get as jest.Mock).mockRejectedValue(new Error('Network error')); |
| |
|
| | const result = await loadServiceKey(url); |
| | expect(result).toBeNull(); |
| | }); |
| |
|
| | it('should validate service key format', async () => { |
| | const invalidServiceKey = { invalid: 'key' }; |
| | const result = await loadServiceKey(JSON.stringify(invalidServiceKey)); |
| | expect(result).toEqual(invalidServiceKey); |
| | }); |
| |
|
| | it('should handle escaped newlines in private key from AWS Secrets Manager', async () => { |
| | const serviceKeyWithEscapedNewlines = { |
| | ...mockServiceKey, |
| | private_key: '-----BEGIN PRIVATE KEY-----\\ntest-key\\n-----END PRIVATE KEY-----', |
| | }; |
| | const jsonString = JSON.stringify(serviceKeyWithEscapedNewlines); |
| |
|
| | const result = await loadServiceKey(jsonString); |
| | expect(result).not.toBeNull(); |
| | expect(result?.private_key).toBe( |
| | '-----BEGIN PRIVATE KEY-----\ntest-key\n-----END PRIVATE KEY-----', |
| | ); |
| | }); |
| |
|
| | it('should handle double-escaped newlines in private key', async () => { |
| | |
| | |
| | const serviceKeyWithEscapedNewlines = { |
| | ...mockServiceKey, |
| | private_key: '-----BEGIN PRIVATE KEY-----\\ntest-key\\n-----END PRIVATE KEY-----', |
| | }; |
| | |
| | const jsonString = JSON.stringify(serviceKeyWithEscapedNewlines); |
| |
|
| | const result = await loadServiceKey(jsonString); |
| | expect(result).not.toBeNull(); |
| | expect(result?.private_key).toBe( |
| | '-----BEGIN PRIVATE KEY-----\ntest-key\n-----END PRIVATE KEY-----', |
| | ); |
| | }); |
| |
|
| | it('should handle private key without any newlines', async () => { |
| | const serviceKeyWithoutNewlines = { |
| | ...mockServiceKey, |
| | private_key: '-----BEGIN PRIVATE KEY-----test-key-----END PRIVATE KEY-----', |
| | }; |
| | const jsonString = JSON.stringify(serviceKeyWithoutNewlines); |
| |
|
| | const result = await loadServiceKey(jsonString); |
| | expect(result).not.toBeNull(); |
| | expect(result?.private_key).toBe( |
| | '-----BEGIN PRIVATE KEY-----\ntest-key\n-----END PRIVATE KEY-----', |
| | ); |
| | }); |
| |
|
| | it('should not modify private key that already has proper formatting', async () => { |
| | const jsonString = JSON.stringify(mockServiceKey); |
| |
|
| | const result = await loadServiceKey(jsonString); |
| | expect(result).not.toBeNull(); |
| | expect(result?.private_key).toBe(mockServiceKey.private_key); |
| | }); |
| |
|
| | it('should handle base64 encoded service key', async () => { |
| | const jsonString = JSON.stringify(mockServiceKey); |
| | const base64Encoded = Buffer.from(jsonString).toString('base64'); |
| |
|
| | const result = await loadServiceKey(base64Encoded); |
| | expect(result).not.toBeNull(); |
| | expect(result).toEqual(mockServiceKey); |
| | }); |
| |
|
| | it('should handle base64 encoded service key with escaped newlines', async () => { |
| | const serviceKeyWithEscapedNewlines = { |
| | ...mockServiceKey, |
| | private_key: '-----BEGIN PRIVATE KEY-----\\ntest-key\\n-----END PRIVATE KEY-----', |
| | }; |
| | const jsonString = JSON.stringify(serviceKeyWithEscapedNewlines); |
| | const base64Encoded = Buffer.from(jsonString).toString('base64'); |
| |
|
| | const result = await loadServiceKey(base64Encoded); |
| | expect(result).not.toBeNull(); |
| | expect(result?.private_key).toBe( |
| | '-----BEGIN PRIVATE KEY-----\ntest-key\n-----END PRIVATE KEY-----', |
| | ); |
| | }); |
| |
|
| | it('should handle invalid base64 strings gracefully', async () => { |
| | |
| | const invalidBase64 = 'SGVsbG8gV29ybGQ='; |
| |
|
| | const result = await loadServiceKey(invalidBase64); |
| | expect(result).toBeNull(); |
| | }); |
| | }); |
| |
|