refactor: migrate to Elysia and enhance security middleware
- Replaced Express with Elysia for improved performance and type safety - Integrated Elysia middleware for rate limiting, security headers, and request validation - Refactored security utilities to work with Elysia's context and request handling - Updated token management and validation logic - Added comprehensive security headers and input sanitization - Simplified server initialization and error handling - Updated documentation with new setup and configuration details
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { config } from "dotenv";
|
||||
import path from "path";
|
||||
import { TEST_CONFIG } from "../config/__tests__/test.config";
|
||||
import {
|
||||
beforeAll,
|
||||
afterAll,
|
||||
@@ -12,6 +11,25 @@ import {
|
||||
test,
|
||||
} from "bun:test";
|
||||
|
||||
// Type definitions for mocks
|
||||
type MockFn = ReturnType<typeof mock>;
|
||||
|
||||
interface MockInstance {
|
||||
mock: {
|
||||
calls: unknown[][];
|
||||
results: unknown[];
|
||||
instances: unknown[];
|
||||
lastCall?: unknown[];
|
||||
};
|
||||
}
|
||||
|
||||
// Test configuration
|
||||
const TEST_CONFIG = {
|
||||
TEST_JWT_SECRET: "test_jwt_secret_key_that_is_at_least_32_chars",
|
||||
TEST_TOKEN: "test_token_that_is_at_least_32_chars_long",
|
||||
TEST_CLIENT_IP: "127.0.0.1",
|
||||
};
|
||||
|
||||
// Load test environment variables
|
||||
config({ path: path.resolve(process.cwd(), ".env.test") });
|
||||
|
||||
@@ -23,34 +41,10 @@ beforeAll(() => {
|
||||
process.env.TEST_TOKEN = TEST_CONFIG.TEST_TOKEN;
|
||||
|
||||
// Configure console output for tests
|
||||
const originalConsoleError = console.error;
|
||||
const originalConsoleWarn = console.warn;
|
||||
const originalConsoleLog = console.log;
|
||||
|
||||
// Suppress console output during tests unless explicitly enabled
|
||||
if (!process.env.DEBUG) {
|
||||
console.error = mock(() => {});
|
||||
console.warn = mock(() => {});
|
||||
console.log = mock(() => {});
|
||||
}
|
||||
|
||||
// Store original console methods for cleanup
|
||||
(global as any).__ORIGINAL_CONSOLE__ = {
|
||||
error: originalConsoleError,
|
||||
warn: originalConsoleWarn,
|
||||
log: originalConsoleLog,
|
||||
};
|
||||
});
|
||||
|
||||
// Global test teardown
|
||||
afterAll(() => {
|
||||
// Restore original console methods
|
||||
const originalConsole = (global as any).__ORIGINAL_CONSOLE__;
|
||||
if (originalConsole) {
|
||||
console.error = originalConsole.error;
|
||||
console.warn = originalConsole.warn;
|
||||
console.log = originalConsole.log;
|
||||
delete (global as any).__ORIGINAL_CONSOLE__;
|
||||
console.error = mock(() => { });
|
||||
console.warn = mock(() => { });
|
||||
console.log = mock(() => { });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,7 +52,7 @@ afterAll(() => {
|
||||
beforeEach(() => {
|
||||
// Clear all mock function calls
|
||||
const mockFns = Object.values(mock).filter(
|
||||
(value) => typeof value === "function",
|
||||
(value): value is MockFn => typeof value === "function" && "mock" in value,
|
||||
);
|
||||
mockFns.forEach((mockFn) => {
|
||||
if (mockFn.mock) {
|
||||
@@ -70,100 +64,80 @@ beforeEach(() => {
|
||||
});
|
||||
});
|
||||
|
||||
// Custom test environment setup
|
||||
const setupTestEnvironment = () => {
|
||||
return {
|
||||
// Mock WebSocket for SSE tests
|
||||
mockWebSocket: () => {
|
||||
const mockWs = {
|
||||
on: mock(() => {}),
|
||||
send: mock(() => {}),
|
||||
close: mock(() => {}),
|
||||
};
|
||||
return mockWs;
|
||||
// Custom test utilities
|
||||
const testUtils = {
|
||||
// Mock WebSocket for SSE tests
|
||||
mockWebSocket: () => ({
|
||||
on: mock(() => { }),
|
||||
send: mock(() => { }),
|
||||
close: mock(() => { }),
|
||||
readyState: 1,
|
||||
OPEN: 1,
|
||||
removeAllListeners: mock(() => { }),
|
||||
}),
|
||||
|
||||
// Mock HTTP response for API tests
|
||||
mockResponse: () => {
|
||||
const res = {
|
||||
status: mock(() => res),
|
||||
json: mock(() => res),
|
||||
send: mock(() => res),
|
||||
end: mock(() => res),
|
||||
setHeader: mock(() => res),
|
||||
writeHead: mock(() => res),
|
||||
write: mock(() => true),
|
||||
removeHeader: mock(() => res),
|
||||
};
|
||||
return res;
|
||||
},
|
||||
|
||||
// Mock HTTP request for API tests
|
||||
mockRequest: (overrides: Record<string, unknown> = {}) => ({
|
||||
headers: { "content-type": "application/json" },
|
||||
body: {},
|
||||
query: {},
|
||||
params: {},
|
||||
ip: TEST_CONFIG.TEST_CLIENT_IP,
|
||||
method: "GET",
|
||||
path: "/api/test",
|
||||
is: mock((type: string) => type === "application/json"),
|
||||
...overrides,
|
||||
}),
|
||||
|
||||
// Create test client for SSE tests
|
||||
createTestClient: (id = "test-client") => ({
|
||||
id,
|
||||
ip: TEST_CONFIG.TEST_CLIENT_IP,
|
||||
connectedAt: new Date(),
|
||||
send: mock(() => { }),
|
||||
rateLimit: {
|
||||
count: 0,
|
||||
lastReset: Date.now(),
|
||||
},
|
||||
connectionTime: Date.now(),
|
||||
}),
|
||||
|
||||
// Mock HTTP response for API tests
|
||||
mockResponse: () => {
|
||||
const res: any = {};
|
||||
res.status = mock(() => res);
|
||||
res.json = mock(() => res);
|
||||
res.send = mock(() => res);
|
||||
res.end = mock(() => res);
|
||||
res.setHeader = mock(() => res);
|
||||
res.writeHead = mock(() => res);
|
||||
res.write = mock(() => true);
|
||||
res.removeHeader = mock(() => res);
|
||||
return res;
|
||||
},
|
||||
// Create test event for SSE tests
|
||||
createTestEvent: (type = "test_event", data: unknown = {}) => ({
|
||||
event_type: type,
|
||||
data,
|
||||
origin: "test",
|
||||
time_fired: new Date().toISOString(),
|
||||
context: { id: "test" },
|
||||
}),
|
||||
|
||||
// Mock HTTP request for API tests
|
||||
mockRequest: (overrides = {}) => {
|
||||
return {
|
||||
headers: { "content-type": "application/json" },
|
||||
body: {},
|
||||
query: {},
|
||||
params: {},
|
||||
ip: TEST_CONFIG.TEST_CLIENT_IP,
|
||||
method: "GET",
|
||||
path: "/api/test",
|
||||
is: mock((type: string) => type === "application/json"),
|
||||
...overrides,
|
||||
};
|
||||
},
|
||||
// Create test entity for Home Assistant tests
|
||||
createTestEntity: (entityId = "test.entity", state = "on") => ({
|
||||
entity_id: entityId,
|
||||
state,
|
||||
attributes: {},
|
||||
last_changed: new Date().toISOString(),
|
||||
last_updated: new Date().toISOString(),
|
||||
}),
|
||||
|
||||
// Create test client for SSE tests
|
||||
createTestClient: (id: string = "test-client") => ({
|
||||
id,
|
||||
ip: TEST_CONFIG.TEST_CLIENT_IP,
|
||||
connectedAt: new Date(),
|
||||
send: mock(() => {}),
|
||||
rateLimit: {
|
||||
count: 0,
|
||||
lastReset: Date.now(),
|
||||
},
|
||||
connectionTime: Date.now(),
|
||||
}),
|
||||
|
||||
// Create test event for SSE tests
|
||||
createTestEvent: (type: string = "test_event", data: any = {}) => ({
|
||||
event_type: type,
|
||||
data,
|
||||
origin: "test",
|
||||
time_fired: new Date().toISOString(),
|
||||
context: { id: "test" },
|
||||
}),
|
||||
|
||||
// Create test entity for Home Assistant tests
|
||||
createTestEntity: (
|
||||
entityId: string = "test.entity",
|
||||
state: string = "on",
|
||||
) => ({
|
||||
entity_id: entityId,
|
||||
state,
|
||||
attributes: {},
|
||||
last_changed: new Date().toISOString(),
|
||||
last_updated: new Date().toISOString(),
|
||||
}),
|
||||
|
||||
// Helper to wait for async operations
|
||||
wait: (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)),
|
||||
};
|
||||
// Helper to wait for async operations
|
||||
wait: (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)),
|
||||
};
|
||||
|
||||
// Export test utilities
|
||||
export const testUtils = setupTestEnvironment();
|
||||
|
||||
// Export Bun test utilities
|
||||
export { beforeAll, afterAll, beforeEach, describe, expect, it, mock, test };
|
||||
|
||||
// Make test utilities available globally
|
||||
(global as any).testUtils = testUtils;
|
||||
(global as any).describe = describe;
|
||||
(global as any).it = it;
|
||||
(global as any).test = test;
|
||||
(global as any).expect = expect;
|
||||
(global as any).beforeAll = beforeAll;
|
||||
(global as any).afterAll = afterAll;
|
||||
(global as any).beforeEach = beforeEach;
|
||||
(global as any).mock = mock;
|
||||
// Export test utilities and Bun test functions
|
||||
export { beforeAll, afterAll, beforeEach, describe, expect, it, mock, test, testUtils };
|
||||
|
||||
Reference in New Issue
Block a user