Files
wehub-resource-sync f9447f8e5f
CI / typecheck · build · test · bundle-size (push) Failing after 1s
CI / clean-room dependency closure smoke (push) Failing after 1s
CI / server-runtime e2e (docker · pg + valkey) (push) Failing after 2s
Deploy Install Scripts / deploy (push) Failing after 2s
Windows / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:07:03 +08:00

239 lines
8.1 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, afterAll, spyOn, mock } from 'bun:test';
import { logger } from '../../src/utils/logger.js';
// Capture the real middleware module before mock.module mutates the live
// namespace, then re-register the snapshot in afterAll. bun's mock.module is
// process-global and mock.restore() does NOT undo it, so without this the stub
// createMiddleware leaks into later files (e.g. CORS + v1-routes server tests).
import * as realMiddleware from '../../src/services/worker/http/middleware.js';
const realMiddlewareSnapshot = { ...realMiddleware };
mock.module('../../src/services/worker/http/middleware.js', () => ({
createMiddleware: () => [],
requireLocalhost: (_req: any, _res: any, next: any) => next(),
summarizeRequestBody: () => 'test body',
}));
import { Server } from '../../src/services/server/Server.js';
import type { ServerOptions } from '../../src/services/server/Server.js';
let loggerSpies: ReturnType<typeof spyOn>[] = [];
describe('Hook Execution E2E', () => {
let server: Server;
let testPort: number;
let mockOptions: ServerOptions;
beforeEach(() => {
loggerSpies = [
spyOn(logger, 'info').mockImplementation(() => {}),
spyOn(logger, 'debug').mockImplementation(() => {}),
spyOn(logger, 'warn').mockImplementation(() => {}),
spyOn(logger, 'error').mockImplementation(() => {}),
];
mockOptions = {
getInitializationComplete: () => true,
getMcpReady: () => true,
onShutdown: mock(() => Promise.resolve()),
onRestart: mock(() => Promise.resolve()),
workerPath: '/test/worker-service.cjs',
getAiStatus: () => ({
provider: 'claude',
authMethod: 'cli',
lastInteraction: null,
}),
};
testPort = 40000 + Math.floor(Math.random() * 10000);
});
afterEach(async () => {
loggerSpies.forEach(spy => spy.mockRestore());
if (server && server.getHttpServer()) {
try {
await server.close();
} catch {
// Ignore errors on cleanup
}
}
mock.restore();
});
afterAll(() => {
mock.module('../../src/services/worker/http/middleware.js', () => realMiddlewareSnapshot);
});
describe('health and readiness endpoints', () => {
it('should return 200 with status ok from /api/health', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/health`);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.status).toBe('ok');
expect(body.initialized).toBe(true);
expect(body.mcpReady).toBe(true);
expect(body.platform).toBeDefined();
expect(typeof body.pid).toBe('number');
});
it('should return 200 with status ready from /api/readiness when initialized', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/readiness`);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.status).toBe('ready');
});
it('should return 503 from /api/readiness when not initialized', async () => {
const uninitializedOptions: ServerOptions = {
getInitializationComplete: () => false,
getMcpReady: () => false,
onShutdown: mock(() => Promise.resolve()),
onRestart: mock(() => Promise.resolve()),
workerPath: '/test/worker-service.cjs',
getAiStatus: () => ({ provider: 'claude', authMethod: 'cli', lastInteraction: null }),
};
server = new Server(uninitializedOptions);
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/readiness`);
expect(response.status).toBe(503);
const body = await response.json();
expect(body.status).toBe('initializing');
expect(body.message).toBeDefined();
});
it('should return version from /api/version', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/version`);
expect(response.status).toBe(200);
const body = await response.json();
expect(body.version).toBeDefined();
expect(typeof body.version).toBe('string');
});
});
describe('server lifecycle', () => {
it('should start and stop cleanly', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const httpServer = server.getHttpServer();
expect(httpServer).not.toBeNull();
expect(httpServer!.listening).toBe(true);
const response = await fetch(`http://127.0.0.1:${testPort}/api/health`);
expect(response.status).toBe(200);
try {
await server.close();
} catch (e: any) {
if (e.code !== 'ERR_SERVER_NOT_RUNNING') {
throw e;
}
}
const httpServerAfter = server.getHttpServer();
if (httpServerAfter) {
expect(httpServerAfter.listening).toBe(false);
}
});
it('should reflect initialization state changes dynamically', async () => {
let isInitialized = false;
const dynamicOptions: ServerOptions = {
getInitializationComplete: () => isInitialized,
getMcpReady: () => true,
onShutdown: mock(() => Promise.resolve()),
onRestart: mock(() => Promise.resolve()),
workerPath: '/test/worker-service.cjs',
getAiStatus: () => ({ provider: 'claude', authMethod: 'cli', lastInteraction: null }),
};
server = new Server(dynamicOptions);
await server.listen(testPort, '127.0.0.1');
let response = await fetch(`http://127.0.0.1:${testPort}/api/health`);
let body = await response.json();
expect(body.initialized).toBe(false);
isInitialized = true;
response = await fetch(`http://127.0.0.1:${testPort}/api/health`);
body = await response.json();
expect(body.initialized).toBe(true);
});
});
describe('route handling', () => {
it('should return 404 for unknown routes after finalizeRoutes', async () => {
server = new Server(mockOptions);
server.finalizeRoutes();
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/nonexistent`);
expect(response.status).toBe(404);
const body = await response.json();
expect(body.error).toBe('NotFound');
});
it('should accept JSON content type for POST requests', async () => {
server = new Server(mockOptions);
server.finalizeRoutes();
await server.listen(testPort, '127.0.0.1');
const response = await fetch(`http://127.0.0.1:${testPort}/api/test-json`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ test: 'data' })
});
expect(response.status).toBe(404);
});
});
describe('privacy tag handling simulation', () => {
it('should demonstrate privacy skip flow for entirely private prompt', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const { stripMemoryTags } = await import('../../src/utils/tag-stripping.js');
const privatePrompt = '<private>secret command</private>';
const cleanedPrompt = stripMemoryTags(privatePrompt);
const shouldSkip = !cleanedPrompt || cleanedPrompt.trim() === '';
expect(shouldSkip).toBe(true);
});
it('should demonstrate partial privacy for mixed prompts', async () => {
server = new Server(mockOptions);
await server.listen(testPort, '127.0.0.1');
const { stripMemoryTags } = await import('../../src/utils/tag-stripping.js');
const mixedPrompt = '<private>my password is secret123</private> Help me write a function';
const cleanedPrompt = stripMemoryTags(mixedPrompt);
const shouldSkip = !cleanedPrompt || cleanedPrompt.trim() === '';
expect(shouldSkip).toBe(false);
expect(cleanedPrompt.trim()).toBe('Help me write a function');
});
});
});