mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 10:52:08 +01:00
- Add PostgreSQL-specific data types (@db.VarChar, @db.Text, @db.Timestamptz, @db.JsonB, @db.Inet) - Implement comprehensive database constraints via custom migration - Add detailed field-level documentation and enum descriptions - Optimize indexes for common query patterns and company-scoped data - Ensure data integrity with check constraints for positive values and logical time validation - Add partial indexes for performance optimization on failed/pending processing sessions
326 lines
10 KiB
TypeScript
326 lines
10 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
registerSchema,
|
|
loginSchema,
|
|
forgotPasswordSchema,
|
|
resetPasswordSchema,
|
|
sessionFilterSchema,
|
|
companySettingsSchema,
|
|
userUpdateSchema,
|
|
metricsQuerySchema,
|
|
validateInput,
|
|
} from '../../lib/validation';
|
|
|
|
describe('Validation Schemas', () => {
|
|
// Helper for password validation
|
|
const validPassword = 'Password123!';
|
|
const invalidPasswordShort = 'Pass1!';
|
|
const invalidPasswordNoLower = 'PASSWORD123!';
|
|
const invalidPasswordNoUpper = 'password123!';
|
|
const invalidPasswordNoNumber = 'Password!!';
|
|
const invalidPasswordNoSpecial = 'Password123';
|
|
|
|
// Helper for email validation
|
|
const validEmail = 'test@example.com';
|
|
const invalidEmailFormat = 'test@example';
|
|
const invalidEmailTooLong = 'a'.repeat(250) + '@example.com'; // 250 + 11 = 261 chars
|
|
|
|
// Helper for company name validation
|
|
const validCompanyName = 'My Company Inc.';
|
|
const invalidCompanyNameEmpty = '';
|
|
const invalidCompanyNameTooLong = 'A'.repeat(101);
|
|
const invalidCompanyNameChars = 'My Company #$%';
|
|
|
|
describe('registerSchema', () => {
|
|
it('should validate a valid registration object', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: validPassword,
|
|
company: validCompanyName,
|
|
};
|
|
expect(registerSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid email', () => {
|
|
const data = {
|
|
email: invalidEmailFormat,
|
|
password: validPassword,
|
|
company: validCompanyName,
|
|
};
|
|
expect(registerSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid password', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: invalidPasswordShort,
|
|
company: validCompanyName,
|
|
};
|
|
expect(registerSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid company name', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: validPassword,
|
|
company: invalidCompanyNameEmpty,
|
|
};
|
|
expect(registerSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('loginSchema', () => {
|
|
it('should validate a valid login object', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: validPassword,
|
|
};
|
|
expect(loginSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid email', () => {
|
|
const data = {
|
|
email: invalidEmailFormat,
|
|
password: validPassword,
|
|
};
|
|
expect(loginSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an empty password', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: '',
|
|
};
|
|
expect(loginSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('forgotPasswordSchema', () => {
|
|
it('should validate a valid email', () => {
|
|
const data = { email: validEmail };
|
|
expect(forgotPasswordSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid email', () => {
|
|
const data = { email: invalidEmailFormat };
|
|
expect(forgotPasswordSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('resetPasswordSchema', () => {
|
|
it('should validate a valid reset password object', () => {
|
|
const data = {
|
|
token: 'some-valid-token',
|
|
password: validPassword,
|
|
};
|
|
expect(resetPasswordSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an empty token', () => {
|
|
const data = {
|
|
token: '',
|
|
password: validPassword,
|
|
};
|
|
expect(resetPasswordSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid password', () => {
|
|
const data = {
|
|
token: 'some-valid-token',
|
|
password: invalidPasswordShort,
|
|
};
|
|
expect(resetPasswordSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('sessionFilterSchema', () => {
|
|
it('should validate a valid session filter object', () => {
|
|
const data = {
|
|
search: 'query',
|
|
sentiment: 'POSITIVE',
|
|
category: 'SCHEDULE_HOURS',
|
|
startDate: '2023-01-01T00:00:00Z',
|
|
endDate: '2023-01-31T23:59:59Z',
|
|
page: 1,
|
|
limit: 20,
|
|
};
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should validate with only optional fields', () => {
|
|
const data = {};
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid sentiment', () => {
|
|
const data = { sentiment: 'INVALID' };
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid category', () => {
|
|
const data = { category: 'INVALID_CATEGORY' };
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid date format', () => {
|
|
const data = { startDate: '2023-01-01' }; // Missing time
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate page less than 1', () => {
|
|
const data = { page: 0 };
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate limit greater than 100', () => {
|
|
const data = { limit: 101 };
|
|
expect(sessionFilterSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('companySettingsSchema', () => {
|
|
it('should validate a valid company settings object', () => {
|
|
const data = {
|
|
name: validCompanyName,
|
|
csvUrl: 'http://example.com/data.csv',
|
|
csvUsername: 'user',
|
|
csvPassword: 'password',
|
|
sentimentAlert: 0.5,
|
|
dashboardOpts: { theme: 'dark' },
|
|
};
|
|
expect(companySettingsSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid CSV URL', () => {
|
|
const data = {
|
|
name: validCompanyName,
|
|
csvUrl: 'invalid-url',
|
|
};
|
|
expect(companySettingsSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid company name', () => {
|
|
const data = {
|
|
name: invalidCompanyNameEmpty,
|
|
csvUrl: 'http://example.com/data.csv',
|
|
};
|
|
expect(companySettingsSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate sentimentAlert out of range', () => {
|
|
const data = {
|
|
name: validCompanyName,
|
|
csvUrl: 'http://example.com/data.csv',
|
|
sentimentAlert: 1.1,
|
|
};
|
|
expect(companySettingsSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('userUpdateSchema', () => {
|
|
it('should validate a valid user update object with all fields', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
role: 'ADMIN',
|
|
password: validPassword,
|
|
};
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should validate a valid user update object with only email', () => {
|
|
const data = { email: validEmail };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should validate a valid user update object with only role', () => {
|
|
const data = { role: 'USER' };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should validate a valid user update object with only password', () => {
|
|
const data = { password: validPassword };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid email', () => {
|
|
const data = { email: invalidEmailFormat };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid role', () => {
|
|
const data = { role: 'SUPERUSER' };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid password', () => {
|
|
const data = { password: invalidPasswordShort };
|
|
expect(userUpdateSchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('metricsQuerySchema', () => {
|
|
it('should validate a valid metrics query object', () => {
|
|
const data = {
|
|
startDate: '2023-01-01T00:00:00Z',
|
|
endDate: '2023-01-31T23:59:59Z',
|
|
companyId: 'a1b2c3d4-e5f6-7890-1234-567890abcdef',
|
|
};
|
|
expect(metricsQuerySchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should validate with only optional fields', () => {
|
|
const data = {};
|
|
expect(metricsQuerySchema.safeParse(data).success).toBe(true);
|
|
});
|
|
|
|
it('should invalidate an invalid date format', () => {
|
|
const data = { startDate: '2023-01-01' };
|
|
expect(metricsQuerySchema.safeParse(data).success).toBe(false);
|
|
});
|
|
|
|
it('should invalidate an invalid companyId format', () => {
|
|
const data = { companyId: 'invalid-uuid' };
|
|
expect(metricsQuerySchema.safeParse(data).success).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('validateInput', () => {
|
|
const testSchema = registerSchema; // Using registerSchema for validateInput tests
|
|
|
|
it('should return success true and data for valid input', () => {
|
|
const data = {
|
|
email: validEmail,
|
|
password: validPassword,
|
|
company: validCompanyName,
|
|
};
|
|
const result = validateInput(testSchema, data);
|
|
expect(result.success).toBe(true);
|
|
expect((result as any).data).toEqual(data);
|
|
});
|
|
|
|
it('should return success false and errors for invalid input', () => {
|
|
const data = {
|
|
email: invalidEmailFormat,
|
|
password: invalidPasswordShort,
|
|
company: invalidCompanyNameEmpty,
|
|
};
|
|
const result = validateInput(testSchema, data);
|
|
expect(result.success).toBe(false);
|
|
expect((result as any).errors).toEqual(expect.arrayContaining([
|
|
'email: Invalid email format',
|
|
'password: Password must be at least 12 characters long',
|
|
'company: Company name is required',
|
|
]));
|
|
});
|
|
|
|
it('should handle non-ZodError errors gracefully', () => {
|
|
const mockSchema = {
|
|
parse: () => { throw new Error('Some unexpected error'); }
|
|
} as any;
|
|
const result = validateInput(mockSchema, {});
|
|
expect(result.success).toBe(false);
|
|
expect((result as any).errors).toEqual(['Invalid input']);
|
|
});
|
|
});
|
|
});
|