ctrlshiftleft
Version:
AI-powered toolkit for embedding QA and security testing into development workflows
274 lines (214 loc) • 9.47 kB
JavaScript
/**
* Unit tests for QA validation middleware
*/
const { expect, describe, test, beforeEach } = require('@jest/globals');
// Mock for express response
const mockResponse = () => {
const res = {};
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
return res;
};
describe('QA Validation Middleware', () => {
let validation;
beforeEach(() => {
// Reset modules before each test
jest.resetModules();
// Import the validation module - TypeScript file requires ts-jest
validation = require('../../../src/middleware/qa-validation');
});
describe('validateInput', () => {
test('should validate text input without issues', () => {
const result = validation.validateInput('Hello world', 'text');
expect(result.valid).toBe(true);
expect(result.securityIssues).toHaveLength(0);
});
test('should detect potential XSS in text input', () => {
const result = validation.validateInput('<script>alert("XSS")</script>', 'text');
expect(result.valid).toBe(false);
expect(result.securityIssues.length).toBeGreaterThan(0);
expect(result.securityIssues[0]).toContain('unsafe code');
});
test('should detect potential SQL injection', () => {
const result = validation.validateInput("DROP TABLE users;", 'text');
expect(result.valid).toBe(false);
expect(result.securityIssues.length).toBeGreaterThan(0);
expect(result.securityIssues[0]).toContain('SQL');
});
test('should validate URL format', () => {
const validResult = validation.validateInput('https://example.com', 'url');
const invalidResult = validation.validateInput('not-a-url', 'url');
expect(validResult.valid).toBe(true);
expect(invalidResult.valid).toBe(false);
});
test('should detect unsafe URL', () => {
const result = validation.validateInput('javascript:alert(1)', 'url');
expect(result.valid).toBe(false);
expect(result.securityIssues.length).toBeGreaterThan(0);
});
test('should validate email format', () => {
const validResult = validation.validateInput('user@example.com', 'email');
const invalidResult = validation.validateInput('not-an-email', 'email');
expect(validResult.valid).toBe(true);
expect(invalidResult.valid).toBe(false);
});
test('should validate password strength', () => {
const weakResult = validation.validateInput('123456', 'password');
const strongResult = validation.validateInput('StrongP@ssw0rd123', 'password');
expect(weakResult.valid).toBe(false);
expect(strongResult.valid).toBe(true);
});
test('should detect common passwords', () => {
const result = validation.validateInput('password123', 'password');
expect(result.valid).toBe(false);
expect(result.securityIssues.length).toBeGreaterThan(0);
expect(result.securityIssues[0]).toContain('common');
});
});
describe('validateRequestBody', () => {
test('should validate multiple fields in request body', () => {
const body = {
name: 'John Doe',
email: 'john@example.com',
password: 'SecureP@ss123'
};
const rules = {
name: { type: 'text', required: true },
email: { type: 'email', required: true },
password: { type: 'password', required: true }
};
const result = validation.validateRequestBody(body, rules);
expect(result.isValid).toBe(true);
expect(result.errors).toEqual({});
expect(result.securityIssues).toHaveLength(0);
});
test('should detect missing required fields', () => {
const body = {
name: 'John Doe',
// Missing email
password: 'SecureP@ss123'
};
const rules = {
name: { type: 'text', required: true },
email: { type: 'email', required: true },
password: { type: 'password', required: true }
};
const result = validation.validateRequestBody(body, rules);
expect(result.isValid).toBe(false);
expect(result.errors).toHaveProperty('email');
});
test('should skip validation for optional empty fields', () => {
const body = {
name: 'John Doe',
email: 'john@example.com',
// Optional field is empty
phone: ''
};
const rules = {
name: { type: 'text', required: true },
email: { type: 'email', required: true },
phone: { type: 'text', required: false }
};
const result = validation.validateRequestBody(body, rules);
expect(result.isValid).toBe(true);
});
test('should validate optional fields when provided', () => {
const body = {
name: 'John Doe',
email: 'john@example.com',
phone: '<script>alert(1)</script>' // Optional but insecure
};
const rules = {
name: { type: 'text', required: true },
email: { type: 'email', required: true },
phone: { type: 'text', required: false }
};
const result = validation.validateRequestBody(body, rules);
expect(result.isValid).toBe(false);
expect(result.errors).toHaveProperty('phone');
expect(result.securityIssues.length).toBeGreaterThan(0);
});
});
describe('createValidationMiddleware', () => {
test('should create middleware that passes for valid requests', () => {
const middleware = validation.createValidationMiddleware({
name: { type: 'text', required: true },
email: { type: 'email', required: true }
});
const req = { body: { name: 'John Doe', email: 'john@example.com' } };
const res = mockResponse();
const next = jest.fn();
middleware(req, res, next);
expect(next).toHaveBeenCalled();
expect(res.status).not.toHaveBeenCalled();
});
test('should create middleware that rejects invalid requests', () => {
const middleware = validation.createValidationMiddleware({
name: { type: 'text', required: true },
email: { type: 'email', required: true }
});
const req = { body: { name: 'John Doe', email: 'not-an-email' } };
const res = mockResponse();
const next = jest.fn();
middleware(req, res, next);
expect(next).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalled();
});
test('should add validation result to request object', () => {
const middleware = validation.createValidationMiddleware({
name: { type: 'text', required: true }
});
const req = { body: { name: 'John Doe' } };
const res = mockResponse();
const next = jest.fn();
middleware(req, res, next);
expect(req).toHaveProperty('validationResult');
expect(next).toHaveBeenCalled();
});
});
describe('convenience validation functions', () => {
test('validateUrl should provide formatted validation results', () => {
const validResult = validation.validateUrl('https://example.com');
const invalidResult = validation.validateUrl('javascript:alert(1)');
expect(validResult.isValid).toBe(true);
expect(validResult.errorMessage).toBe('');
expect(invalidResult.isValid).toBe(false);
expect(invalidResult.errorMessage).not.toBe('');
expect(invalidResult.hasSecurity).toBe(true);
});
test('validateEmail should provide formatted validation results', () => {
const validResult = validation.validateEmail('user@example.com');
const invalidResult = validation.validateEmail('not-an-email');
expect(validResult.isValid).toBe(true);
expect(validResult.errorMessage).toBe('');
expect(invalidResult.isValid).toBe(false);
expect(invalidResult.errorMessage).not.toBe('');
});
test('validatePassword should check strength and provide suggestions', () => {
// This test assumes the implementation of the validatePassword function
// that provides strength metrics and suggestions
if (typeof validation.validatePassword === 'function') {
const result = validation.validatePassword('weak123');
expect(result).toHaveProperty('isValid');
expect(result).toHaveProperty('strength');
expect(result).toHaveProperty('suggestions');
} else {
console.warn('validatePassword function not implemented, skipping test');
}
});
test('validateJson should validate JSON strings and objects', () => {
// This test assumes the implementation of the validateJson function
if (typeof validation.validateJson === 'function') {
const validStringResult = validation.validateJson('{"name":"John"}');
const validObjectResult = validation.validateJson({name: "John"});
const invalidResult = validation.validateJson('{not valid json}');
expect(validStringResult.isValid).toBe(true);
expect(validObjectResult.isValid).toBe(true);
expect(invalidResult.isValid).toBe(false);
} else {
console.warn('validateJson function not implemented, skipping test');
}
});
});
});