guardz-event
Version:
Type-safe event handling with runtime validation using guardz for unsafe data from 3rd parties
254 lines • 11 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference types="jest" />
const safe_event_never_throws_1 = require("../utils/safe-event-never-throws");
// Mock type guards for testing
const isUserMessage = (data) => {
return (typeof data === 'object' &&
data !== null &&
typeof data.id === 'number' &&
typeof data.name === 'string' &&
typeof data.email === 'string' &&
typeof data.isActive === 'boolean');
};
const isStockUpdate = (data) => {
return (typeof data === 'object' &&
data !== null &&
typeof data.symbol === 'string' &&
typeof data.price === 'number' &&
typeof data.change === 'number');
};
const isClickData = (data) => {
return (typeof data === 'object' &&
data !== null &&
typeof data.clientX === 'number' &&
typeof data.clientY === 'number' &&
typeof data.button === 'number');
};
describe('Legacy API', () => {
describe('safePostMessage', () => {
it('should return success result for valid message', () => {
const handler = (0, safe_event_never_throws_1.safePostMessage)({
guard: isUserMessage,
allowedOrigins: ['https://trusted-domain.com'],
});
const validEvent = new MessageEvent('message', {
origin: 'https://trusted-domain.com',
data: {
id: 1,
name: 'John',
email: 'john@example.com',
isActive: true,
},
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({
id: 1,
name: 'John',
email: 'john@example.com',
isActive: true,
});
}
});
it('should return error result for untrusted origin', () => {
const handler = (0, safe_event_never_throws_1.safePostMessage)({
guard: isUserMessage,
allowedOrigins: ['https://trusted-domain.com'],
});
const maliciousEvent = new MessageEvent('message', {
origin: 'https://malicious-site.com',
data: { id: 1, name: 'Hacker', email: 'hack@evil.com', isActive: true },
});
const result = handler(maliciousEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR);
if (result.status === safe_event_never_throws_1.Status.ERROR) {
expect(result.code).toBe(403);
expect(result.message).toContain('not allowed');
}
});
it('should return error result for invalid data', () => {
const handler = (0, safe_event_never_throws_1.safePostMessage)({
guard: isUserMessage,
allowedOrigins: ['https://trusted-domain.com'],
});
const invalidEvent = new MessageEvent('message', {
origin: 'https://trusted-domain.com',
data: {
id: 'not-a-number',
name: 'John',
email: 'john@example.com',
isActive: true,
},
});
const result = handler(invalidEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR);
if (result.status === safe_event_never_throws_1.Status.ERROR) {
expect(result.code).toBe(500);
expect(result.message).toContain('Validation failed');
}
});
});
describe('safeWebSocket', () => {
it('should return success result for valid WebSocket message', () => {
const handler = (0, safe_event_never_throws_1.safeWebSocket)({
guard: isStockUpdate,
});
const validEvent = new MessageEvent('message', {
data: { symbol: 'AAPL', price: 150.25, change: 2.5 },
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({
symbol: 'AAPL',
price: 150.25,
change: 2.5,
});
}
});
it('should return error result for invalid WebSocket data', () => {
const handler = (0, safe_event_never_throws_1.safeWebSocket)({
guard: isStockUpdate,
});
const invalidEvent = new MessageEvent('message', {
data: { symbol: 'AAPL', price: 'not-a-number', change: 2.5 },
});
const result = handler(invalidEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR);
if (result.status === safe_event_never_throws_1.Status.ERROR) {
expect(result.code).toBe(500);
expect(result.message).toContain('Validation failed');
}
});
});
describe('safeDOMEvent', () => {
it('should return success result for valid DOM event', () => {
const handler = (0, safe_event_never_throws_1.safeDOMEvent)({
guard: isClickData,
});
const validEvent = new MouseEvent('click', {
clientX: 100,
clientY: 200,
button: 0,
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({ clientX: 100, clientY: 200, button: 0 });
}
});
it('should return error result for invalid DOM event data', () => {
const handler = (0, safe_event_never_throws_1.safeDOMEvent)({
guard: isClickData,
});
const invalidEvent = new Event('click');
const result = handler(invalidEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR);
if (result.status === safe_event_never_throws_1.Status.ERROR) {
expect(result.code).toBe(500);
expect(result.message).toContain('Validation failed');
}
});
});
describe('safeEventSource', () => {
it('should return success result for valid EventSource message', () => {
const handler = (0, safe_event_never_throws_1.safeEventSource)({
guard: isStockUpdate,
});
const validEvent = new MessageEvent('message', {
data: { symbol: 'AAPL', price: 150.25, change: 2.5 },
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({
symbol: 'AAPL',
price: 150.25,
change: 2.5,
});
}
});
});
describe('safeCustomEvent', () => {
it('should return success result for valid custom event', () => {
const handler = (0, safe_event_never_throws_1.safeCustomEvent)({
guard: isStockUpdate,
});
const validEvent = new CustomEvent('stock-update', {
detail: { symbol: 'AAPL', price: 150.25, change: 2.5 },
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({
symbol: 'AAPL',
price: 150.25,
change: 2.5,
});
}
});
});
describe('safeStorageEvent', () => {
it('should return success result for valid storage event', () => {
const handler = (0, safe_event_never_throws_1.safeStorageEvent)({
guard: (data) => {
return (typeof data === 'object' &&
data !== null &&
typeof data.key === 'string' &&
typeof data.url === 'string');
},
});
const validEvent = new StorageEvent('storage', {
key: 'user_preferences',
oldValue: 'old_value',
newValue: 'new_value',
url: 'https://example.com',
});
const result = handler(validEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
if (result.status === safe_event_never_throws_1.Status.SUCCESS) {
expect(result.data).toEqual({
key: 'user_preferences',
oldValue: 'old_value',
newValue: 'new_value',
url: 'https://example.com',
});
}
});
});
describe('Tolerance Mode', () => {
it('should handle tolerance mode correctly', () => {
const onError = jest.fn();
const handler = (0, safe_event_never_throws_1.safeWebSocket)({
guard: isStockUpdate,
tolerance: true,
onError,
});
const partialEvent = new MessageEvent('message', {
data: { symbol: 'AAPL', price: 150.25, change: 'not-a-number' },
});
const result = handler(partialEvent);
// In tolerance mode, it should still return success but with warnings
expect(result.status).toBe(safe_event_never_throws_1.Status.SUCCESS);
expect(onError).toHaveBeenCalled();
});
});
describe('Error Handling', () => {
it('should handle null events gracefully', () => {
const handler = (0, safe_event_never_throws_1.safePostMessage)({
guard: isUserMessage,
allowedOrigins: ['https://trusted-domain.com'],
});
const nullEvent = null;
const result = handler(nullEvent);
expect(result.status).toBe(safe_event_never_throws_1.Status.ERROR);
if (result.status === safe_event_never_throws_1.Status.ERROR) {
expect(result.code).toBe(500);
expect(result.message).toContain('Invalid event');
}
});
});
});
//# sourceMappingURL=legacy-api.test.js.map