import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { buildServer } from '../src/server'; import { FastifyInstance } from 'fastify'; import fs from 'fs'; import path from 'path'; import { config } from '../src/config'; describe('Proxy Integration Test', () => { let server: FastifyInstance; beforeEach(() => { config.proxyMode = 'ollama'; server = buildServer(); // In vitest we can mock the global fetch global.fetch = vi.fn(); }); afterEach(async () => { await server.close(); vi.restoreAllMocks(); }); it('proxies request and rewrites XML response to tool_calls', async () => { // Read fixtures const requestFixturePath = path.join(__dirname, 'fixtures', 'openclaw-like-request.json'); const responseFixturePath = path.join(__dirname, 'fixtures', 'ollama-xml-response.json'); const requestJson = JSON.parse(fs.readFileSync(requestFixturePath, 'utf8')); const responseJson = JSON.parse(fs.readFileSync(responseFixturePath, 'utf8')); // Mock fetch to return the ollama-xml-response.json (global.fetch as any).mockResolvedValue({ ok: true, text: async () => JSON.stringify(responseJson), json: async () => responseJson }); const response = await server.inject({ method: 'POST', url: '/api/chat', payload: requestJson }); expect(response.statusCode).toBe(200); const body = JSON.parse(response.payload); // Verify proxy forwarded it expect(global.fetch).toHaveBeenCalledTimes(1); const fetchArgs = (global.fetch as any).mock.calls[0]; expect(fetchArgs[0]).toContain('/api/chat'); const upstreamBody = JSON.parse(fetchArgs[1].body); expect(upstreamBody.model).toBe('hotwa/qwen35-9b-agent:latest'); // Verify response was rewritten expect(body.message.content).toBe(""); expect(body.message.tool_calls).toBeDefined(); expect(body.message.tool_calls).toHaveLength(1); expect(body.message.tool_calls[0].function.name).toBe('read'); expect(body.message.tool_calls[0].function.arguments).toEqual({ path: "/tmp/test.txt" }); }); it('rejects streaming requests cleanly', async () => { const requestFixturePath = path.join(__dirname, 'fixtures', 'openclaw-like-request.json'); const requestJson = JSON.parse(fs.readFileSync(requestFixturePath, 'utf8')); requestJson.stream = true; const response = await server.inject({ method: 'POST', url: '/api/chat', payload: requestJson }); expect(response.statusCode).toBe(400); const body = JSON.parse(response.payload); expect(body.error).toContain('Streaming is not supported'); expect(global.fetch).not.toHaveBeenCalled(); }); });