qgenutils
Version:
A security-first Node.js utility library providing authentication, HTTP operations, URL processing, validation, datetime formatting, and template rendering. Designed as a lightweight alternative to heavy npm packages with comprehensive error handling and
286 lines (234 loc) • 11.4 kB
JavaScript
// Integration tests covering how individual utilities interact when combined in
// typical request workflows. The focus here is cross-module cooperation rather
// than isolated unit behavior.
const utils = require('../../index');
const {
formatDateTime,
formatDuration,
calculateContentLength,
buildCleanHeaders,
ensureProtocol,
normalizeUrlOrigin,
requireFields,
checkPassportAuth
} = utils;
describe('Module Integration Tests', () => { // verifies utilities work together
describe('HTTP and URL Integration', () => { // checks proxy flow from URL to headers
// verifies should process URL and calculate content length for API request
test('should process URL and calculate content length for API request', () => {
const url = 'api.example.com/users';
const body = { name: 'John', email: 'john@example.com' };
// Process URL
const processedUrl = ensureProtocol(url);
expect(processedUrl).toBe('https://api.example.com/users'); // ensure protocol added
// Calculate content length for request body
const contentLength = calculateContentLength(body);
expect(contentLength).toBe(Buffer.byteLength(JSON.stringify(body), 'utf8').toString()); // compare calculated length
// Build clean headers
const headers = buildCleanHeaders({
'content-type': 'application/json',
'host': 'evil.com'
}, 'POST', body);
expect(headers['content-length']).toBe(contentLength); // header set correctly
expect(headers['host']).toBeUndefined(); // host stripped
});
// verifies should normalize URLs and build appropriate headers
test('should normalize URLs and build appropriate headers', () => {
const urls = [
'HTTPS://API.Example.com/v1',
'api.example.com/v1',
'HTTP://api.example.com/v1'
];
const normalizedOrigins = urls.map(normalizeUrlOrigin);
// All should normalize to the same origin
expect(normalizedOrigins[0]).toBe('https://api.example.com'); // https url normalized
expect(normalizedOrigins[1]).toBe('https://api.example.com'); // missing protocol normalized
expect(normalizedOrigins[2]).toBe('http://api.example.com'); // http kept
// Headers should be clean for any method
const headers = buildCleanHeaders({
'authorization': 'Bearer token',
'x-target-url': normalizedOrigins[0]
}, 'GET', null);
expect(headers['authorization']).toBe('Bearer token'); // auth header kept
expect(headers['x-target-url']).toBeUndefined(); // target url stripped
});
});
describe('Validation and Authentication Integration', () => { // confirms auth and validation interplay
// verifies should validate required fields and check authentication together
test('should validate required fields and check authentication together', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
// Mock authenticated request
const mockAuthReq = {
user: { id: 1, name: 'John' },
isAuthenticated: jest.fn().mockReturnValue(true),
body: { title: 'Test Post', content: 'Content here' }
};
// Check authentication first
const isAuth = checkPassportAuth(mockAuthReq);
expect(isAuth).toBe(true); // authentication succeeded
// Then validate required fields
const isValid = requireFields(mockAuthReq.body, ['title', 'content'], mockRes); // (reordered parameters to match obj, fields, res)
expect(isValid).toBe(true); // fields valid
expect(mockRes.status).not.toHaveBeenCalled(); // no error status
});
// verifies should handle unauthenticated user with valid fields
test('should handle unauthenticated user with valid fields', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
const mockUnauthReq = {
isAuthenticated: jest.fn().mockReturnValue(false),
body: { title: 'Test Post', content: 'Content here' }
};
// Authentication fails
const isAuth = checkPassportAuth(mockUnauthReq);
expect(isAuth).toBe(false); // authentication fails
// Fields are valid but auth failed
const isValid = requireFields(mockUnauthReq.body, ['title', 'content'], mockRes); // (reordered parameters to match obj, fields, res)
expect(isValid).toBe(true); // fields still valid
});
});
describe('DateTime and HTTP Integration', () => { // ensures date helpers feed HTTP responses
// verifies should format timestamps and include in HTTP responses
test('should format timestamps and include in HTTP responses', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
const startTime = '2023-12-25T10:00:00.000Z';
const endTime = '2023-12-25T11:30:45.000Z';
// Format timestamps
const formattedStart = formatDateTime(startTime);
const duration = formatDuration(startTime, endTime);
// Create response data
const responseData = {
started_at: formattedStart,
duration: duration,
status: 'completed'
};
// Calculate content length for response
const contentLength = calculateContentLength(responseData);
// Send response
utils.sendJsonResponse(mockRes, 200, responseData);
expect(mockRes.status).toHaveBeenCalledWith(200); // success status returned
expect(mockRes.json).toHaveBeenCalledWith(responseData); // response payload returned
expect(formattedStart).not.toBe('N/A'); // date formatted
expect(duration).toBe('01:30:45'); // duration calculation
});
// verifies should handle malformed dates in HTTP context
test('should handle malformed dates in HTTP context', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
const invalidDate = 'not-a-date';
// Format should return N/A for invalid date
const formatted = formatDateTime(invalidDate);
expect(formatted).toBe('N/A');
// Should still be able to create valid response
const responseData = { timestamp: formatted, error: 'Invalid date provided' };
utils.sendJsonResponse(mockRes, 400, responseData);
expect(mockRes.status).toHaveBeenCalledWith(400);
expect(mockRes.json).toHaveBeenCalledWith(responseData);
});
});
describe('Complete Request Processing Workflow', () => { // simulates real API request lifecycle
// verifies should simulate complete API request processing
test('should simulate complete API request processing', () => {
const mockReq = {
headers: {
'authorization': 'Bearer valid-token',
'content-type': 'application/json',
'host': 'proxy.example.com',
'x-target-url': 'api.service.com/posts'
},
body: {
title: 'New Post',
content: 'Post content here',
published_at: '2023-12-25T10:30:00.000Z'
},
user: { id: 1, name: 'John' },
isAuthenticated: jest.fn().mockReturnValue(true)
};
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
// Step 1: Check authentication
const isAuthenticated = checkPassportAuth(mockReq);
expect(isAuthenticated).toBe(true);
// Step 2: Validate required fields
const fieldsValid = requireFields(mockReq.body, ['title', 'content'], mockRes); // (reordered parameters to match obj, fields, res)
expect(fieldsValid).toBe(true);
// Step 3: Extract and validate required headers
const authHeader = utils.getRequiredHeader(mockReq, mockRes, 'authorization', 401, 'Missing auth');
expect(authHeader).toBe('Bearer valid-token');
// Step 4: Process target URL
const targetUrl = mockReq.headers['x-target-url'];
const processedUrl = ensureProtocol(targetUrl);
expect(processedUrl).toBe('https://api.service.com/posts');
// Step 5: Build clean headers for proxying
const cleanHeaders = buildCleanHeaders(mockReq.headers, 'POST', mockReq.body);
expect(cleanHeaders['authorization']).toBe('Bearer valid-token'); // auth header kept
expect(cleanHeaders['host']).toBeUndefined(); // host removed for proxying
expect(cleanHeaders['x-target-url']).toBeUndefined(); // internal header removed
// Step 6: Format timestamps in response
const formattedDate = formatDateTime(mockReq.body.published_at);
// Step 7: Send successful response
const responseData = {
id: 123,
title: mockReq.body.title,
published_at: formattedDate,
status: 'created'
};
utils.sendJsonResponse(mockRes, 201, responseData);
expect(mockRes.status).toHaveBeenCalledWith(201); // return created status
expect(mockRes.json).toHaveBeenCalledWith(responseData); // send data
});
// verifies should handle complete workflow with validation failure
test('should handle complete workflow with validation failure', () => {
const mockReq = {
headers: { 'authorization': 'Bearer token' },
body: { title: 'Post without content' }, // Missing required content
user: { id: 1 },
isAuthenticated: jest.fn().mockReturnValue(true)
};
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
// Authentication passes
expect(checkPassportAuth(mockReq)).toBe(true); // auth check passes
// Validation fails
const fieldsValid = requireFields(mockReq.body, ['title', 'content'], mockRes); // (reordered parameters to match obj, fields, res)
expect(fieldsValid).toBe(false); // validation fails
expect(mockRes.status).toHaveBeenCalledWith(400); // 400 returned
expect(mockRes.json).toHaveBeenCalledWith({ // error payload returned
error: 'Missing required fields',
missing: ['content']
});
});
// verifies should handle complete workflow with authentication failure
test('should handle complete workflow with authentication failure', () => {
const mockReq = {
headers: { 'content-type': 'application/json' },
body: { title: 'Post', content: 'Content' },
isAuthenticated: jest.fn().mockReturnValue(false)
};
const mockRes = {
status: jest.fn().mockReturnThis(),
json: jest.fn().mockReturnThis()
};
// Authentication fails early
expect(checkPassportAuth(mockReq)).toBe(false); // authentication failure
// Even though fields are valid, auth failed
const fieldsValid = requireFields(mockReq.body, ['title', 'content'], mockRes); // (reordered parameters to match obj, fields, res)
expect(fieldsValid).toBe(true); // fields still valid despite auth failure
// In a real app, we'd return 401 for auth failure before validating fields
});
});
});