diff --git a/tests/integration/claude-code-optional.test.js b/tests/integration/claude-code-optional.test.js index 6dc791e3..ab90bdb5 100644 --- a/tests/integration/claude-code-optional.test.js +++ b/tests/integration/claude-code-optional.test.js @@ -18,7 +18,9 @@ jest.unstable_mockModule('@anthropic-ai/claude-code', () => { }); // Import after mocking -const { ClaudeCodeProvider } = await import('../../src/ai-providers/claude-code.js'); +const { ClaudeCodeProvider } = await import( + '../../src/ai-providers/claude-code.js' +); describe('Claude Code Optional Dependency Integration', () => { describe('when @anthropic-ai/claude-code is not installed', () => { @@ -43,10 +45,14 @@ describe('Claude Code Optional Dependency Integration', () => { const model = client('opus'); // The actual usage should fail with the lazy loading error - await expect(model.doGenerate({ - prompt: [{ role: 'user', content: 'Hello' }], - mode: { type: 'regular' } - })).rejects.toThrow("Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."); + await expect( + model.doGenerate({ + prompt: [{ role: 'user', content: 'Hello' }], + mode: { type: 'regular' } + }) + ).rejects.toThrow( + "Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider." + ); }); it('should provide helpful error message for streaming', async () => { @@ -54,10 +60,14 @@ describe('Claude Code Optional Dependency Integration', () => { const client = provider.getClient({}); const model = client('sonnet'); - await expect(model.doStream({ - prompt: [{ role: 'user', content: 'Hello' }], - mode: { type: 'regular' } - })).rejects.toThrow("Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."); + await expect( + model.doStream({ + prompt: [{ role: 'user', content: 'Hello' }], + mode: { type: 'regular' } + }) + ).rejects.toThrow( + "Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider." + ); }); }); @@ -74,7 +84,7 @@ describe('Claude Code Optional Dependency Integration', () => { // when the actual model is used const provider = new ClaudeCodeProvider(); expect(provider).toBeDefined(); - + // In real usage, ai-services-unified would: // 1. Get the provider instance (works) // 2. Call provider.getClient() (works) @@ -82,4 +92,4 @@ describe('Claude Code Optional Dependency Integration', () => { // 4. Try to generate (fails with clear error) }); }); -}); \ No newline at end of file +}); diff --git a/tests/unit/ai-providers/claude-code.test.js b/tests/unit/ai-providers/claude-code.test.js index 3edff893..92388444 100644 --- a/tests/unit/ai-providers/claude-code.test.js +++ b/tests/unit/ai-providers/claude-code.test.js @@ -1,18 +1,21 @@ import { jest } from '@jest/globals'; // Mock the claude-code SDK module -jest.unstable_mockModule('../../../src/ai-providers/custom-sdk/claude-code/index.js', () => ({ - createClaudeCode: jest.fn(() => { - const provider = (modelId, settings) => ({ - // Mock language model - id: modelId, - settings - }); - provider.languageModel = jest.fn((id, settings) => ({ id, settings })); - provider.chat = provider.languageModel; - return provider; +jest.unstable_mockModule( + '../../../src/ai-providers/custom-sdk/claude-code/index.js', + () => ({ + createClaudeCode: jest.fn(() => { + const provider = (modelId, settings) => ({ + // Mock language model + id: modelId, + settings + }); + provider.languageModel = jest.fn((id, settings) => ({ id, settings })); + provider.chat = provider.languageModel; + return provider; + }) }) -})); +); // Mock the base provider jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({ @@ -27,7 +30,9 @@ jest.unstable_mockModule('../../../src/ai-providers/base-provider.js', () => ({ })); // Import after mocking -const { ClaudeCodeProvider } = await import('../../../src/ai-providers/claude-code.js'); +const { ClaudeCodeProvider } = await import( + '../../../src/ai-providers/claude-code.js' +); describe('ClaudeCodeProvider', () => { let provider; @@ -53,10 +58,12 @@ describe('ClaudeCodeProvider', () => { }); it('should work with any params passed', () => { - expect(() => provider.validateAuth({ - apiKey: 'some-key', - baseURL: 'https://example.com' - })).not.toThrow(); + expect(() => + provider.validateAuth({ + apiKey: 'some-key', + baseURL: 'https://example.com' + }) + ).not.toThrow(); }); }); @@ -91,14 +98,18 @@ describe('ClaudeCodeProvider', () => { describe('error handling', () => { it('should handle client initialization errors', async () => { // Force an error by making createClaudeCode throw - const { createClaudeCode } = await import('../../../src/ai-providers/custom-sdk/claude-code/index.js'); + const { createClaudeCode } = await import( + '../../../src/ai-providers/custom-sdk/claude-code/index.js' + ); createClaudeCode.mockImplementationOnce(() => { throw new Error('Mock initialization error'); }); // Create a new provider instance to use the mocked createClaudeCode const errorProvider = new ClaudeCodeProvider(); - expect(() => errorProvider.getClient({})).toThrow('Mock initialization error'); + expect(() => errorProvider.getClient({})).toThrow( + 'Mock initialization error' + ); }); }); -}); \ No newline at end of file +}); diff --git a/tests/unit/ai-providers/custom-sdk/claude-code/language-model.test.js b/tests/unit/ai-providers/custom-sdk/claude-code/language-model.test.js index d45ae498..5f1813aa 100644 --- a/tests/unit/ai-providers/custom-sdk/claude-code/language-model.test.js +++ b/tests/unit/ai-providers/custom-sdk/claude-code/language-model.test.js @@ -15,21 +15,30 @@ jest.unstable_mockModule('@ai-sdk/provider-utils', () => ({ generateId: jest.fn(() => 'test-id-123') })); -jest.unstable_mockModule('../../../../../src/ai-providers/custom-sdk/claude-code/message-converter.js', () => ({ - convertToClaudeCodeMessages: jest.fn((prompt) => ({ - messagesPrompt: 'converted-prompt', - systemPrompt: 'system' - })) -})); +jest.unstable_mockModule( + '../../../../../src/ai-providers/custom-sdk/claude-code/message-converter.js', + () => ({ + convertToClaudeCodeMessages: jest.fn((prompt) => ({ + messagesPrompt: 'converted-prompt', + systemPrompt: 'system' + })) + }) +); -jest.unstable_mockModule('../../../../../src/ai-providers/custom-sdk/claude-code/json-extractor.js', () => ({ - extractJson: jest.fn((text) => text) -})); +jest.unstable_mockModule( + '../../../../../src/ai-providers/custom-sdk/claude-code/json-extractor.js', + () => ({ + extractJson: jest.fn((text) => text) + }) +); -jest.unstable_mockModule('../../../../../src/ai-providers/custom-sdk/claude-code/errors.js', () => ({ - createAPICallError: jest.fn((opts) => new Error(opts.message)), - createAuthenticationError: jest.fn((opts) => new Error(opts.message)) -})); +jest.unstable_mockModule( + '../../../../../src/ai-providers/custom-sdk/claude-code/errors.js', + () => ({ + createAPICallError: jest.fn((opts) => new Error(opts.message)), + createAuthenticationError: jest.fn((opts) => new Error(opts.message)) + }) +); // This mock will be controlled by tests let mockClaudeCodeModule = null; @@ -41,7 +50,9 @@ jest.unstable_mockModule('@anthropic-ai/claude-code', () => { }); // Import the module under test -const { ClaudeCodeLanguageModel } = await import('../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'); +const { ClaudeCodeLanguageModel } = await import( + '../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js' +); describe('ClaudeCodeLanguageModel', () => { beforeEach(() => { @@ -65,15 +76,21 @@ describe('ClaudeCodeLanguageModel', () => { }); it('should throw NoSuchModelError for invalid model ID', async () => { - expect(() => new ClaudeCodeLanguageModel({ - id: '', - settings: {} - })).toThrow('No such model: '); + expect( + () => + new ClaudeCodeLanguageModel({ + id: '', + settings: {} + }) + ).toThrow('No such model: '); - expect(() => new ClaudeCodeLanguageModel({ - id: null, - settings: {} - })).toThrow('No such model: null'); + expect( + () => + new ClaudeCodeLanguageModel({ + id: null, + settings: {} + }) + ).toThrow('No such model: null'); }); }); @@ -85,19 +102,30 @@ describe('ClaudeCodeLanguageModel', () => { settings: {} }); - await expect(model.doGenerate({ - prompt: [{ role: 'user', content: 'test' }], - mode: { type: 'regular' } - })).rejects.toThrow("Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider."); + await expect( + model.doGenerate({ + prompt: [{ role: 'user', content: 'test' }], + mode: { type: 'regular' } + }) + ).rejects.toThrow( + "Claude Code SDK is not installed. Please install '@anthropic-ai/claude-code' to use the claude-code provider." + ); }); it('should load package successfully when available', async () => { // Mock successful package load const mockQuery = jest.fn(async function* () { - yield { type: 'assistant', message: { content: [{ type: 'text', text: 'Hello' }] } }; - yield { type: 'result', subtype: 'done', usage: { output_tokens: 10, input_tokens: 5 } }; + yield { + type: 'assistant', + message: { content: [{ type: 'text', text: 'Hello' }] } + }; + yield { + type: 'result', + subtype: 'done', + usage: { output_tokens: 10, input_tokens: 5 } + }; }); - + mockClaudeCodeModule = { query: mockQuery, AbortError: class AbortError extends Error {} @@ -105,8 +133,10 @@ describe('ClaudeCodeLanguageModel', () => { // Need to re-import to get fresh module with mocks jest.resetModules(); - const { ClaudeCodeLanguageModel: FreshModel } = await import('../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'); - + const { ClaudeCodeLanguageModel: FreshModel } = await import( + '../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js' + ); + const model = new FreshModel({ id: 'opus', settings: {} @@ -124,24 +154,30 @@ describe('ClaudeCodeLanguageModel', () => { it('should only attempt to load package once', async () => { // Get a fresh import to ensure clean state jest.resetModules(); - const { ClaudeCodeLanguageModel: TestModel } = await import('../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js'); - + const { ClaudeCodeLanguageModel: TestModel } = await import( + '../../../../../src/ai-providers/custom-sdk/claude-code/language-model.js' + ); + const model = new TestModel({ id: 'opus', settings: {} }); // First call should throw - await expect(model.doGenerate({ - prompt: [{ role: 'user', content: 'test' }], - mode: { type: 'regular' } - })).rejects.toThrow("Claude Code SDK is not installed"); + await expect( + model.doGenerate({ + prompt: [{ role: 'user', content: 'test' }], + mode: { type: 'regular' } + }) + ).rejects.toThrow('Claude Code SDK is not installed'); // Second call should also throw without trying to load again - await expect(model.doGenerate({ - prompt: [{ role: 'user', content: 'test' }], - mode: { type: 'regular' } - })).rejects.toThrow("Claude Code SDK is not installed"); + await expect( + model.doGenerate({ + prompt: [{ role: 'user', content: 'test' }], + mode: { type: 'regular' } + }) + ).rejects.toThrow('Claude Code SDK is not installed'); }); }); @@ -163,7 +199,8 @@ describe('ClaudeCodeLanguageModel', () => { expect(warnings[0]).toEqual({ type: 'unsupported-setting', setting: 'temperature', - details: 'Claude Code CLI does not support the temperature parameter. It will be ignored.' + details: + 'Claude Code CLI does not support the temperature parameter. It will be ignored.' }); }); @@ -197,4 +234,4 @@ describe('ClaudeCodeLanguageModel', () => { expect(model.getModel()).toBe('custom-model'); }); }); -}); \ No newline at end of file +});