新增完整的API代理服务架构,支持Gemini、OpenAI和Claude等多种大模型API的统一接入。主要变更包括: 1. 实现策略模式架构,新增provider-strategies.js处理不同API协议 2. 添加适配器层(adapter.js)统一服务接口 3. 实现三种核心模型(Gemini/OpenAI/Claude)的完整支持 4. 添加测试配置和依赖 5. 更新README文档说明新架构和使用方式 6. 新增.gitignore配置和项目元文件
652 lines
26 KiB
JavaScript
652 lines
26 KiB
JavaScript
import { fetch } from 'undici';
|
|
|
|
/**
|
|
* HTTP Integration Tests for API Server
|
|
*
|
|
* This test suite makes actual HTTP requests to a running server instance
|
|
* instead of directly calling the program code. This provides true integration testing.
|
|
*
|
|
* Configuration:
|
|
* - TEST_SERVER_BASE_URL: The base URL of the running server
|
|
* - TEST_API_KEY: The API key to use for authentication (should match server config)
|
|
*
|
|
* Each test can include custom headers to test different scenarios.
|
|
* Make sure the server is running at the specified URL before running tests.
|
|
*/
|
|
|
|
// Test server configuration
|
|
const TEST_SERVER_BASE_URL = 'http://192.168.1.232:3000';
|
|
const TEST_API_KEY = '123456'; // You may need to adjust this based on your server config
|
|
const MODEL_PROVIDER = {
|
|
// Model provider constants
|
|
GEMINI_CLI: 'gemini-cli-oauth',
|
|
OPENAI_CUSTOM: 'openai-custom',
|
|
CLAUDE_CUSTOM: 'claude-custom',
|
|
KIRO_API: 'openai-kiro-oauth',
|
|
}
|
|
|
|
// Real test data for different API formats
|
|
const REAL_TEST_DATA = {
|
|
openai: {
|
|
nonStreamRequest: {
|
|
model: "gemini-2.5-flash",
|
|
messages: [
|
|
{ role: "user", content: "Hello, what is 2+2?" }
|
|
]
|
|
},
|
|
streamRequest: {
|
|
model: "gemini-2.5-flash",
|
|
messages: [
|
|
{ role: "user", content: "Hello, what is 2+2?" }
|
|
],
|
|
stream: true
|
|
}
|
|
},
|
|
gemini: {
|
|
nonStreamRequest: {
|
|
contents: [
|
|
{
|
|
role: "user",
|
|
parts: [{ text: "Hello, what is 2+2?" }]
|
|
}
|
|
]
|
|
},
|
|
streamRequest: {
|
|
contents: [
|
|
{
|
|
role: "user",
|
|
parts: [{ text: "Hello, what is 2+2?" }]
|
|
}
|
|
]
|
|
}
|
|
},
|
|
claude: {
|
|
nonStreamRequest: {
|
|
model: "claude-4-sonnet",
|
|
messages: [
|
|
{ role: "user", content: "Hello, what is 2+2?" }
|
|
]
|
|
},
|
|
streamRequest: {
|
|
model: "claude-4-sonnet",
|
|
messages: [
|
|
{ role: "user", content: "Hello, what is 2+2?" }
|
|
],
|
|
stream: true
|
|
}
|
|
}
|
|
};
|
|
|
|
// To run all integration tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js
|
|
describe('API Integration Tests with HTTP Requests', () => {
|
|
beforeAll(async () => {
|
|
// Test server connectivity
|
|
try {
|
|
const healthResponse = await fetch(`${TEST_SERVER_BASE_URL}/health`);
|
|
const healthData = await healthResponse.json();
|
|
console.log('✓ Server is accessible:', healthData);
|
|
} catch (error) {
|
|
console.warn('⚠ Failed to connect to server:', error.message);
|
|
console.log(' Make sure the server is running at', TEST_SERVER_BASE_URL);
|
|
}
|
|
}, 30000); // Set a higher timeout for beforeAll
|
|
|
|
afterAll(() => {
|
|
// Jest handles test results summary automatically
|
|
});
|
|
|
|
// To run all OpenAI Compatible Endpoints tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI Compatible Endpoints"
|
|
describe('OpenAI Compatible Endpoints', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions non-streaming Gemini"
|
|
test('OpenAI /v1/chat/completions non-streaming Gemini', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI },
|
|
REAL_TEST_DATA.openai.nonStreamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('choices');
|
|
expect(Array.isArray(responseData.choices)).toBe(true);
|
|
expect(responseData.choices.length).toBeGreaterThan(0);
|
|
expect(responseData.choices[0]).toHaveProperty('message');
|
|
expect(responseData.choices[0].message).toHaveProperty('content');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions streaming Gemini"
|
|
test('OpenAI /v1/chat/completions streaming Gemini', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI },
|
|
REAL_TEST_DATA.openai.streamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
|
expect(response.headers.get('cache-control')).toBe('no-cache');
|
|
expect(response.headers.get('connection')).toBe('keep-alive');
|
|
|
|
// Read some of the streaming response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunks = [];
|
|
let chunkCount = 0;
|
|
|
|
try {
|
|
while (chunkCount < 3) { // Read first few chunks
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
chunks.push(chunk);
|
|
chunkCount++;
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
expect(chunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions non-streaming with OpenAI provider"
|
|
test('OpenAI /v1/chat/completions non-streaming with OpenAI provider', async () => {
|
|
REAL_TEST_DATA.openai.nonStreamRequest.model = "deepseek-chat";
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.OPENAI_CUSTOM },
|
|
REAL_TEST_DATA.openai.nonStreamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('choices');
|
|
expect(Array.isArray(responseData.choices)).toBe(true);
|
|
expect(responseData.choices.length).toBeGreaterThan(0);
|
|
expect(responseData.choices[0]).toHaveProperty('message');
|
|
expect(responseData.choices[0].message).toHaveProperty('content');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions streaming with OpenAI provider"
|
|
test('OpenAI /v1/chat/completions streaming with OpenAI provider', async () => {
|
|
REAL_TEST_DATA.openai.streamRequest.model = "deepseek-chat";
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.OPENAI_CUSTOM },
|
|
REAL_TEST_DATA.openai.streamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
|
expect(response.headers.get('cache-control')).toBe('no-cache');
|
|
expect(response.headers.get('connection')).toBe('keep-alive');
|
|
|
|
// Read some of the streaming response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunks = [];
|
|
let chunkCount = 0;
|
|
|
|
try {
|
|
while (chunkCount < 3) { // Read first few chunks
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
chunks.push(chunk);
|
|
chunkCount++;
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
expect(chunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions non-streaming with Claude provider"
|
|
test('OpenAI /v1/chat/completions non-streaming with Claude provider', async () => {
|
|
REAL_TEST_DATA.openai.nonStreamRequest.model = "claude-4-sonnet";
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.CLAUDE_CUSTOM },
|
|
REAL_TEST_DATA.claude.nonStreamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('choices');
|
|
expect(Array.isArray(responseData.choices)).toBe(true);
|
|
expect(responseData.choices.length).toBeGreaterThan(0);
|
|
expect(responseData.choices[0]).toHaveProperty('message');
|
|
expect(responseData.choices[0].message).toHaveProperty('content');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/chat/completions streaming with Claude provider"
|
|
test('OpenAI /v1/chat/completions streaming with Claude provider', async () => {
|
|
REAL_TEST_DATA.openai.nonStreamRequest.model = "claude-4-sonnet";
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.CLAUDE_CUSTOM },
|
|
REAL_TEST_DATA.claude.streamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
|
expect(response.headers.get('cache-control')).toBe('no-cache');
|
|
expect(response.headers.get('connection')).toBe('keep-alive');
|
|
|
|
// Read some of the streaming response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunks = [];
|
|
let chunkCount = 0;
|
|
|
|
try {
|
|
while (chunkCount < 3) { // Read first few chunks
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
chunks.push(chunk);
|
|
chunkCount++;
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
expect(chunks.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// To run all Claude Native Endpoints tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Claude Native Endpoints"
|
|
describe('Claude Native Endpoints', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Claude /v1/messages non-streaming"
|
|
test('Claude /v1/messages non-streaming', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/messages`,
|
|
'POST',
|
|
'anthropic',
|
|
{ 'model-provider': MODEL_PROVIDER.CLAUDE_CUSTOM },
|
|
REAL_TEST_DATA.claude.nonStreamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('content');
|
|
expect(Array.isArray(responseData.content)).toBe(true);
|
|
expect(responseData.content.length).toBeGreaterThan(0);
|
|
expect(responseData.content[0]).toHaveProperty('text');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Claude /v1/messages streaming"
|
|
test('Claude /v1/messages streaming', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/messages`,
|
|
'POST',
|
|
'anthropic',
|
|
{ 'model-provider': MODEL_PROVIDER.CLAUDE_CUSTOM },
|
|
REAL_TEST_DATA.claude.streamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
|
expect(response.headers.get('cache-control')).toBe('no-cache');
|
|
expect(response.headers.get('connection')).toBe('keep-alive');
|
|
|
|
// Read some of the streaming response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunks = [];
|
|
let chunkCount = 0;
|
|
|
|
try {
|
|
while (chunkCount < 3) { // Read first few chunks
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
chunks.push(chunk);
|
|
chunkCount++;
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
expect(chunks.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// To run all Gemini Native Endpoints tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Gemini Native Endpoints"
|
|
describe('Gemini Native Endpoints', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Gemini /v1beta/models/{model}:generateContent"
|
|
test('Gemini /v1beta/models/{model}:generateContent', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1beta/models/gemini-2.5-flash:generateContent`,
|
|
'POST',
|
|
'goog',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI },
|
|
REAL_TEST_DATA.gemini.nonStreamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('candidates');
|
|
expect(Array.isArray(responseData.candidates)).toBe(true);
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Gemini /v1beta/models/{model}:streamGenerateContent"
|
|
test('Gemini /v1beta/models/{model}:streamGenerateContent', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1beta/models/gemini-2.5-flash:streamGenerateContent`,
|
|
'POST',
|
|
'goog',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI },
|
|
REAL_TEST_DATA.gemini.streamRequest
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('text/event-stream');
|
|
expect(response.headers.get('cache-control')).toBe('no-cache');
|
|
expect(response.headers.get('connection')).toBe('keep-alive');
|
|
|
|
// Read some of the streaming response
|
|
const reader = response.body.getReader();
|
|
const decoder = new TextDecoder();
|
|
let chunks = [];
|
|
let chunkCount = 0;
|
|
|
|
try {
|
|
while (chunkCount < 3) { // Read first few chunks
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
const chunk = decoder.decode(value);
|
|
chunks.push(chunk);
|
|
chunkCount++;
|
|
}
|
|
} finally {
|
|
reader.releaseLock();
|
|
}
|
|
|
|
expect(chunks.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
// To run all Model List Endpoints tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Model List Endpoints"
|
|
describe('Model List Endpoints', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/models Gemini"
|
|
test('OpenAI /v1/models Gemini', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI }
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('data');
|
|
expect(Array.isArray(responseData.data)).toBe(true);
|
|
});
|
|
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/models OpenAI"
|
|
test('OpenAI /v1/models OpenAI', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.OPENAI_CUSTOM }
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('data');
|
|
expect(Array.isArray(responseData.data)).toBe(true);
|
|
});
|
|
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "OpenAI /v1/models Claude"
|
|
test('OpenAI /v1/models Claude', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'bearer',
|
|
{ 'model-provider': MODEL_PROVIDER.CLAUDE_CUSTOM }
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('data');
|
|
expect(Array.isArray(responseData.data)).toBe(true);
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Gemini /v1beta/models modelList"
|
|
test('Gemini /v1beta/models modelList', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1beta/models`,
|
|
'GET',
|
|
'goog',
|
|
{ 'model-provider': MODEL_PROVIDER.GEMINI_CLI }
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('models');
|
|
expect(Array.isArray(responseData.models)).toBe(true);
|
|
});
|
|
});
|
|
|
|
// To run all Authentication Tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Authentication Tests API KEY"
|
|
describe('Authentication Tests API KEY', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Reject requests without API key"
|
|
test('Reject requests without API key', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'none',
|
|
{},
|
|
{}
|
|
);
|
|
|
|
expect(response.status).toBe(401);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('error');
|
|
expect(responseData.error.message).toContain('Unauthorized');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Accept query parameter authentication"
|
|
test('Accept query parameter authentication', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'query'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Accept Bearer token authentication"
|
|
test('Accept Bearer token authentication', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'bearer'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Accept x-goog-api-key authentication"
|
|
test('Accept x-goog-api-key authentication', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'goog'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Accept x-api-key authentication for Claude"
|
|
test('Accept x-api-key authentication for Claude', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'GET',
|
|
'anthropic'
|
|
);
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
});
|
|
});
|
|
|
|
// To run all Error Handling Tests:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Error Handling Tests"
|
|
describe('Error Handling Tests', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Handle invalid JSON in request body"
|
|
test('Handle invalid JSON in request body', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/chat/completions`,
|
|
'POST',
|
|
'bearer',
|
|
{},
|
|
'invalid json'
|
|
);
|
|
|
|
expect(response.status).toBe(500);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('error');
|
|
});
|
|
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "Handle unsupported endpoints"
|
|
test('Handle unsupported endpoints', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/unsupported/endpoint`,
|
|
'POST',
|
|
'bearer',
|
|
{},
|
|
{}
|
|
);
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(response.headers.get('content-type')).toContain('application/json');
|
|
|
|
const responseData = await response.json();
|
|
expect(responseData).toHaveProperty('error');
|
|
expect(responseData.error.message).toContain('Not Found');
|
|
});
|
|
});
|
|
|
|
// To run all CORS Headers Test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "CORS Headers Test"
|
|
describe('CORS Headers Test', () => {
|
|
// To run this test:
|
|
// npx jest GeminiCli2API/tests/api-integration.test.js -t "CORS headers support"
|
|
test('CORS headers support', async () => {
|
|
const response = await makeRequest(
|
|
`${TEST_SERVER_BASE_URL}/v1/models`,
|
|
'OPTIONS',
|
|
'none',
|
|
{
|
|
'Origin': 'http://localhost:3000',
|
|
'Access-Control-Request-Method': 'POST',
|
|
'Access-Control-Request-Headers': 'Content-Type, Authorization'
|
|
}
|
|
);
|
|
|
|
// CORS preflight should return 200 or 204
|
|
expect([200, 204]).toContain(response.status);
|
|
});
|
|
});
|
|
});
|
|
|
|
|
|
// Helper function: Make a request with authentication and custom headers
|
|
async function makeRequest(url, method, authType = 'none', customHeaders = {}, body = null) {
|
|
const headers = {
|
|
'Content-Type': 'application/json',
|
|
...customHeaders
|
|
};
|
|
|
|
if (authType === 'bearer') {
|
|
headers['Authorization'] = `Bearer ${TEST_API_KEY}`;
|
|
} else if (authType === 'goog') {
|
|
headers['x-goog-api-key'] = TEST_API_KEY;
|
|
} else if (authType === 'anthropic') {
|
|
headers['x-api-key'] = TEST_API_KEY;
|
|
} else if (authType === 'query') {
|
|
url = `${url}?key=${TEST_API_KEY}`;
|
|
}
|
|
|
|
const options = {
|
|
method,
|
|
headers
|
|
};
|
|
|
|
if (body) {
|
|
options.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
}
|
|
|
|
return await fetch(url, options);
|
|
}
|