Saving all progress

This commit is contained in:
Luke Hagar
2025-03-19 22:47:50 -05:00
parent 5c6e8a1e4f
commit 00593b402b
14988 changed files with 2598505 additions and 1 deletions

View File

@@ -0,0 +1,225 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { startServers } from '../../src/server.js';
import fetch, { RequestInit } from 'node-fetch';
import { OpenAPIV3_1 } from 'openapi-types';
interface HAREntry {
request: {
method: string;
url: string;
headers: Array<{ name: string; value: string }>;
queryString: Array<{ name: string; value: string }>;
postData?: {
text: string;
mimeType: string;
};
};
response: {
status: number;
headers: Array<{ name: string; value: string }>;
content: {
text: string;
mimeType: string;
};
};
}
interface HAR {
log: {
entries: HAREntry[];
};
}
interface User {
id: number;
name: string;
}
describe('Arbiter Integration Tests', () => {
const targetPort = 3001;
const proxyPort = 3002;
const docsPort = 3003;
let targetServer: any;
let proxyServer: any;
let docsServer: any;
// Create a mock target API
const targetApi = new Hono();
// Setup test endpoints
targetApi.get('/users', (c) => {
return c.json([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]);
});
targetApi.post('/users', async (c) => {
const body = await c.req.json();
return c.json({ id: 3, ...body }, 201);
});
targetApi.get('/users/:id', (c) => {
const id = c.req.param('id');
return c.json({ id: parseInt(id), name: 'John Doe' });
});
targetApi.get('/secure', (c) => {
const apiKey = c.req.header('x-api-key');
if (apiKey !== 'test-key') {
return c.json({ error: 'Unauthorized' }, 401);
}
return c.json({ message: 'Secret data' });
});
beforeAll(async () => {
// Start the target API server
targetServer = serve({
fetch: targetApi.fetch,
port: targetPort
});
// Start Arbiter servers
const servers = await startServers({
target: `http://localhost:${targetPort}`,
proxyPort,
docsPort,
verbose: false
});
proxyServer = servers.proxyServer;
docsServer = servers.docsServer;
// Wait a bit to ensure servers are ready
await new Promise(resolve => setTimeout(resolve, 1000));
});
afterAll(() => {
targetServer?.close();
proxyServer?.close();
docsServer?.close();
});
it('should proxy basic GET request and record in HAR', async () => {
const response = await fetch(`http://localhost:${proxyPort}/users`);
expect(response.status).toBe(200);
const users = await response.json() as User[];
expect(users).toHaveLength(2);
expect(users[0].name).toBe('John Doe');
// Check HAR recording
const harResponse = await fetch(`http://localhost:${docsPort}/har`);
const har = await harResponse.json() as HAR;
expect(har.log.entries).toHaveLength(1);
expect(har.log.entries[0].request.method).toBe('GET');
expect(har.log.entries[0].request.url).toBe(`http://localhost:${targetPort}/users`);
expect(har.log.entries[0].response.status).toBe(200);
});
it('should record POST request with body in HAR', async () => {
const response = await fetch(`http://localhost:${proxyPort}/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Bob Wilson' })
});
expect(response.status).toBe(201);
const newUser = await response.json() as User;
expect(newUser.name).toBe('Bob Wilson');
// Check HAR recording
const harResponse = await fetch(`http://localhost:${docsPort}/har`);
const har = await harResponse.json() as HAR;
const postEntry = har.log.entries.find(e => e.request.method === 'POST');
expect(postEntry).toBeDefined();
expect(postEntry?.request.postData?.text).toBe(JSON.stringify({ name: 'Bob Wilson' }));
expect(postEntry?.response.status).toBe(201);
});
it('should generate OpenAPI spec with paths and schemas', async () => {
// Make some requests to generate OpenAPI spec
await fetch(`http://localhost:${proxyPort}/users`);
await fetch(`http://localhost:${proxyPort}/users/1`);
await fetch(`http://localhost:${proxyPort}/users`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Test User' })
});
// Get OpenAPI spec
const specResponse = await fetch(`http://localhost:${docsPort}/openapi.json`);
const spec = await specResponse.json() as OpenAPIV3_1.Document;
// Validate paths
expect(spec.paths?.['/users']).toBeDefined();
expect(spec.paths?.['/users']?.get).toBeDefined();
expect(spec.paths?.['/users']?.post).toBeDefined();
expect(spec.paths?.['/users/{id}']?.get).toBeDefined();
// Validate schemas
expect(spec.components?.schemas).toBeDefined();
const userSchema = spec.components?.schemas?.User as OpenAPIV3_1.SchemaObject;
expect(userSchema).toBeDefined();
expect(userSchema.properties?.id).toBeDefined();
expect(userSchema.properties?.name).toBeDefined();
});
it('should handle query parameters', async () => {
await fetch(`http://localhost:${proxyPort}/users?limit=10&offset=0`);
const harResponse = await fetch(`http://localhost:${docsPort}/har`);
const har = await harResponse.json() as HAR;
const entry = har.log.entries.find(e => e.request.url.includes('?limit=10'));
expect(entry).toBeDefined();
expect(entry?.request.queryString).toEqual([
{ name: 'limit', value: '10' },
{ name: 'offset', value: '0' }
]);
const specResponse = await fetch(`http://localhost:${docsPort}/openapi.json`);
const spec = await specResponse.json() as OpenAPIV3_1.Document;
const parameters = spec.paths?.['/users']?.get?.parameters as OpenAPIV3_1.ParameterObject[];
expect(parameters).toBeDefined();
expect(parameters).toContainEqual({
name: 'limit',
in: 'query',
schema: { type: 'string' }
});
});
it('should handle security schemes', async () => {
await fetch(`http://localhost:${proxyPort}/secure`, {
headers: {
'x-api-key': 'test-key'
}
});
const specResponse = await fetch(`http://localhost:${docsPort}/openapi.json`);
const spec = await specResponse.json() as OpenAPIV3_1.Document;
// Check security scheme definition
expect(spec.components?.securitySchemes).toBeDefined();
const apiKeyAuth = spec.components?.securitySchemes?.apiKey_ as OpenAPIV3_1.ApiKeySecurityScheme;
expect(apiKeyAuth).toBeDefined();
expect(apiKeyAuth.type).toBe('apiKey');
expect(apiKeyAuth.in).toBe('header');
expect(apiKeyAuth.name).toBe('x-api-key');
// Check security requirement on endpoint
const securityRequirements = spec.paths?.['/secure']?.get?.security;
expect(securityRequirements).toBeDefined();
expect(securityRequirements).toContainEqual({
apiKey_: []
});
});
});