import { jest } from '@jest/globals';
// Mock the OpenAI-compatible creation
const mockCreateOpenAICompatible = jest.fn(() => jest.fn());
jest.unstable_mockModule('@ai-sdk/openai-compatible', () => ({
createOpenAICompatible: mockCreateOpenAICompatible
}));
// Mock utils
jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({
log: jest.fn(),
resolveEnvVariable: jest.fn((key) => process.env[key]),
findProjectRoot: jest.fn(() => process.cwd()),
isEmpty: jest.fn(() => false)
}));
jest.unstable_mockModule('../../../scripts/modules/config-manager.js', () => ({
isProxyEnabled: jest.fn(() => false),
getAnonymousTelemetryEnabled: jest.fn(() => true),
setSuppressConfigWarnings: jest.fn(),
isConfigWarningSuppressed: jest.fn(() => false)
}));
// Import after mocking
const { ZAIProvider } = await import('../../../src/ai-providers/zai.js');
const { ZAICodingProvider } = await import(
'../../../src/ai-providers/zai-coding.js'
);
describe('ZAI Provider', () => {
let provider;
beforeEach(() => {
jest.clearAllMocks();
provider = new ZAIProvider();
});
describe('Configuration', () => {
it('should have correct base configuration', () => {
expect(provider.name).toBe('Z.ai');
expect(provider.apiKeyEnvVar).toBe('ZAI_API_KEY');
expect(provider.requiresApiKey).toBe(true);
expect(provider.defaultBaseURL).toBe('https://api.z.ai/api/paas/v4/');
expect(provider.supportsStructuredOutputs).toBe(true);
});
});
describe('Token Parameter Handling', () => {
it('should not include max_tokens in requests', () => {
// ZAI API rejects max_tokens parameter (error code 1210)
const result = provider.prepareTokenParam('glm-4.6', 2000);
// Should return empty object instead of { maxOutputTokens: 2000 }
expect(result).toEqual({});
});
it('should return empty object even with undefined maxTokens', () => {
const result = provider.prepareTokenParam('glm-4.6', undefined);
expect(result).toEqual({});
});
it('should return empty object even with very large maxTokens', () => {
// ZAI may have lower limits than other providers
const result = provider.prepareTokenParam('glm-4.6', 204800);
expect(result).toEqual({});
});
});
describe('API Key Handling', () => {
it('should require API key', () => {
expect(provider.isRequiredApiKey()).toBe(true);
expect(provider.getRequiredApiKeyName()).toBe('ZAI_API_KEY');
});
it('should validate when API key is missing', () => {
expect(() => provider.validateAuth({})).toThrow(
'Z.ai API key is required'
);
});
it('should pass validation when API key is provided', () => {
expect(() => provider.validateAuth({ apiKey: 'test-key' })).not.toThrow();
});
});
});
describe('ZAI Coding Provider', () => {
let provider;
beforeEach(() => {
jest.clearAllMocks();
provider = new ZAICodingProvider();
});
describe('Configuration', () => {
it('should have correct base configuration', () => {
expect(provider.name).toBe('Z.ai (Coding Plan)');
expect(provider.apiKeyEnvVar).toBe('ZAI_API_KEY');
expect(provider.requiresApiKey).toBe(true);
expect(provider.defaultBaseURL).toBe(
'https://api.z.ai/api/coding/paas/v4/'
);
expect(provider.supportsStructuredOutputs).toBe(true);
});
});
describe('Token Parameter Handling', () => {
it('should not include max_tokens in requests', () => {
// ZAI Coding API also rejects max_tokens parameter
const result = provider.prepareTokenParam('glm-4.6', 2000);
// Should return empty object instead of { maxOutputTokens: 2000 }
expect(result).toEqual({});
});
it('should return empty object even with undefined maxTokens', () => {
const result = provider.prepareTokenParam('glm-4.6', undefined);
expect(result).toEqual({});
});
});
});