stock-bot/libs/core/handlers/test/base-handler-http.test.ts

272 lines
No EOL
7.8 KiB
TypeScript

import { describe, it, expect, mock, beforeEach, afterEach, spyOn } from 'bun:test';
import { BaseHandler } from '../src/base/BaseHandler';
import type { IServiceContainer, ExecutionContext } from '@stock-bot/types';
import * as utils from '@stock-bot/utils';
// Mock fetch
const mockFetch = mock();
class TestHandler extends BaseHandler {
async testGet(url: string, options?: any) {
return this.http.get(url, options);
}
async testPost(url: string, data?: any, options?: any) {
return this.http.post(url, data, options);
}
async testPut(url: string, data?: any, options?: any) {
return this.http.put(url, data, options);
}
async testDelete(url: string, options?: any) {
return this.http.delete(url, options);
}
}
describe('BaseHandler HTTP Methods', () => {
let handler: TestHandler;
let mockServices: IServiceContainer;
beforeEach(() => {
mockServices = {
cache: null,
globalCache: null,
queueManager: null,
proxy: null,
browser: null,
mongodb: null,
postgres: null,
questdb: null,
logger: {
info: mock(),
debug: mock(),
error: mock(),
warn: mock(),
} as any,
} as IServiceContainer;
handler = new TestHandler(mockServices, 'TestHandler');
// Mock utils.fetch
spyOn(utils, 'fetch').mockImplementation(mockFetch);
mockFetch.mockReset();
});
afterEach(() => {
// spyOn automatically restores
});
describe('GET requests', () => {
it('should make GET requests with fetch', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
json: async () => ({ data: 'test' }),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testGet('https://api.example.com/data');
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data',
expect.objectContaining({
method: 'GET',
logger: expect.any(Object),
})
);
});
it('should pass custom options to GET requests', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testGet('https://api.example.com/data', {
headers: { 'Authorization': 'Bearer token' },
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data',
expect.objectContaining({
headers: { 'Authorization': 'Bearer token' },
method: 'GET',
logger: expect.any(Object),
})
);
});
});
describe('POST requests', () => {
it('should make POST requests with JSON data', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
json: async () => ({ success: true }),
};
mockFetch.mockResolvedValue(mockResponse);
const data = { name: 'test', value: 123 };
await handler.testPost('https://api.example.com/create', data);
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/create',
expect.objectContaining({
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
logger: expect.any(Object),
})
);
});
it('should merge custom headers in POST requests', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testPost('https://api.example.com/create', { test: 'data' }, {
headers: { 'X-Custom': 'value' },
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/create',
expect.objectContaining({
method: 'POST',
body: JSON.stringify({ test: 'data' }),
headers: {
'Content-Type': 'application/json',
'X-Custom': 'value',
},
logger: expect.any(Object),
})
);
});
});
describe('PUT requests', () => {
it('should make PUT requests with JSON data', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
const data = { id: 1, name: 'updated' };
await handler.testPut('https://api.example.com/update/1', data);
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/update/1',
expect.objectContaining({
method: 'PUT',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
logger: expect.any(Object),
})
);
});
it('should handle PUT requests with custom options', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testPut('https://api.example.com/update', { data: 'test' }, {
headers: { 'If-Match': 'etag' },
timeout: 5000,
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/update',
expect.objectContaining({
method: 'PUT',
body: JSON.stringify({ data: 'test' }),
headers: {
'Content-Type': 'application/json',
'If-Match': 'etag',
},
timeout: 5000,
logger: expect.any(Object),
})
);
});
});
describe('DELETE requests', () => {
it('should make DELETE requests', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testDelete('https://api.example.com/delete/1');
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/delete/1',
expect.objectContaining({
method: 'DELETE',
logger: expect.any(Object),
})
);
});
it('should pass options to DELETE requests', async () => {
const mockResponse = {
ok: true,
status: 200,
statusText: 'OK',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
await handler.testDelete('https://api.example.com/delete/1', {
headers: { 'Authorization': 'Bearer token' },
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/delete/1',
expect.objectContaining({
headers: { 'Authorization': 'Bearer token' },
method: 'DELETE',
logger: expect.any(Object),
})
);
});
});
describe('Error handling', () => {
it('should propagate fetch errors', async () => {
mockFetch.mockRejectedValue(new Error('Network error'));
await expect(handler.testGet('https://api.example.com/data')).rejects.toThrow('Network error');
});
it('should handle non-ok responses', async () => {
const mockResponse = {
ok: false,
status: 404,
statusText: 'Not Found',
headers: new Headers(),
};
mockFetch.mockResolvedValue(mockResponse);
const response = await handler.testGet('https://api.example.com/missing');
expect(response.ok).toBe(false);
expect(response.status).toBe(404);
});
});
});