/ tests / unit / claude / client.test.ts
client.test.ts
  1  /**
  2   * Tests for Claude API client
  3   */
  4  import { describe, it, expect, beforeEach, vi } from 'vitest';
  5  import { ClaudeClient, ClaudeAPIError, getClaudeClient, resetClaudeClient } from '../../../src/lib/claude/client';
  6  
  7  describe('ClaudeClient', () => {
  8    let client: ClaudeClient;
  9  
 10    beforeEach(() => {
 11      client = new ClaudeClient();
 12    });
 13  
 14    describe('setApiKey', () => {
 15      it('sets the API key', () => {
 16        client.setApiKey('sk-test-key');
 17        expect(client.isConfigured()).toBe(true);
 18      });
 19  
 20      it('allows null to clear the key', () => {
 21        client.setApiKey('sk-test-key');
 22        client.setApiKey(null);
 23        expect(client.isConfigured()).toBe(false);
 24      });
 25    });
 26  
 27    describe('isConfigured', () => {
 28      it('returns false when no key is set', () => {
 29        expect(client.isConfigured()).toBe(false);
 30      });
 31  
 32      it('returns true when key is set', () => {
 33        client.setApiKey('sk-test-key');
 34        expect(client.isConfigured()).toBe(true);
 35      });
 36  
 37      it('returns false for empty string', () => {
 38        client.setApiKey('');
 39        expect(client.isConfigured()).toBe(false);
 40      });
 41    });
 42  
 43    describe('getApiKeyMasked', () => {
 44      it('returns null when no key is set', () => {
 45        expect(client.getApiKeyMasked()).toBe(null);
 46      });
 47  
 48      it('masks long API keys', () => {
 49        client.setApiKey('sk-ant-api03-very-long-key-here');
 50        const masked = client.getApiKeyMasked();
 51        expect(masked).toMatch(/^sk-a\.\.\.here$/);
 52      });
 53  
 54      it('returns **** for short keys', () => {
 55        client.setApiKey('short');
 56        expect(client.getApiKeyMasked()).toBe('****');
 57      });
 58    });
 59  
 60    describe('setModel', () => {
 61      it('sets the model', () => {
 62        // No direct way to test this without making a request
 63        // Just verify it doesn't throw
 64        expect(() => client.setModel('claude-3-opus')).not.toThrow();
 65      });
 66    });
 67  
 68    describe('sendMessage', () => {
 69      it('throws error when not configured', async () => {
 70        await expect(
 71          client.sendMessage([{ role: 'user', content: 'test' }])
 72        ).rejects.toThrow('Claude API key not configured');
 73      });
 74    });
 75  });
 76  
 77  describe('ClaudeAPIError', () => {
 78    it('creates error with message only', () => {
 79      const error = new ClaudeAPIError('Test error');
 80      expect(error.message).toBe('Test error');
 81      expect(error.name).toBe('ClaudeAPIError');
 82      expect(error.statusCode).toBeUndefined();
 83      expect(error.errorType).toBeUndefined();
 84    });
 85  
 86    it('creates error with status code', () => {
 87      const error = new ClaudeAPIError('Test error', 401);
 88      expect(error.statusCode).toBe(401);
 89    });
 90  
 91    it('creates error with error type', () => {
 92      const error = new ClaudeAPIError('Test error', 400, 'invalid_request');
 93      expect(error.errorType).toBe('invalid_request');
 94    });
 95  });
 96  
 97  describe('getClaudeClient', () => {
 98    beforeEach(() => {
 99      resetClaudeClient();
100    });
101  
102    it('returns a ClaudeClient instance', () => {
103      const client = getClaudeClient();
104      expect(client).toBeInstanceOf(ClaudeClient);
105    });
106  
107    it('returns the same instance on subsequent calls', () => {
108      const client1 = getClaudeClient();
109      const client2 = getClaudeClient();
110      expect(client1).toBe(client2);
111    });
112  });
113  
114  describe('resetClaudeClient', () => {
115    it('creates a new instance after reset', () => {
116      const client1 = getClaudeClient();
117      client1.setApiKey('test-key');
118  
119      resetClaudeClient();
120  
121      const client2 = getClaudeClient();
122      expect(client2.isConfigured()).toBe(false);
123    });
124  });