UNPKG

docudb

Version:

Document-based NoSQL database for NodeJS

332 lines 13.6 kB
import { Database, Schema } from '../index.js'; import { expect } from 'chai'; import { cleanTestDataDir } from './utils.js'; describe('DocuDB - Pattern Validation', () => { let db; const testDbName = 'testPatternValidation'; beforeEach(async () => { await cleanTestDataDir(testDbName); db = new Database({ name: testDbName, compression: false }); await db.initialize(); }); afterEach(async () => { await cleanTestDataDir(testDbName); }); describe('Custom ID Validation', () => { it('should support custom ID format validation', async () => { // Create schema with custom ID format validation const productSchema = new Schema({ name: { type: 'string', required: true }, _id: { type: 'string', default: () => `PROD-${Date.now()}-${Math.floor(Math.random() * 1000)}`, validate: { pattern: /^PROD-\d+-\d{1,3}$/ // Custom ID format validation } } }); const products = db.collection('products', { schema: productSchema }); // Insert a document with auto-generated ID that matches the format const product = await products.insertOne({ name: 'Format Validated Product' }); // Verify custom ID was generated and matches the format expect(product).to.have.property('_id'); expect(product._id).to.match(/^PROD-\d+-\d{1,3}$/); // Try to insert a document with an ID that doesn't match the format try { await products.insertOne({ _id: 'INVALID-FORMAT', name: 'Invalid ID Format Product' }); expect.fail('Should have thrown validation error for invalid ID format'); } catch (error) { expect(error.message).to.include('Does not match the required pattern'); } }); }); describe('Email Validation', () => { it('should validate email format correctly', async () => { const userSchema = new Schema({ email: { type: 'string', required: true, validate: { pattern: /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/, message: 'Invalid email format' } } }); const users = db.collection('users', { schema: userSchema }); // Valid email formats const validEmails = [ 'test@example.com', 'user.name@domain.com', 'user-name@domain.co.uk', 'user123@subdomain.domain.org' ]; for (const email of validEmails) { const user = await users.insertOne({ email }); expect(user).to.have.property('_id'); expect(user.email).to.equal(email); } // Invalid email formats const invalidEmails = [ 'plaintext', 'missing@domain', '@domain.com', 'user@.com', 'user@domain..com', 'user name@domain.com' ]; for (const email of invalidEmails) { try { await users.insertOne({ email }); expect.fail(`Should have rejected invalid email: ${email}`); } catch (error) { expect(error.message).to.include('Invalid email format'); } } }); }); describe('Phone Number Validation', () => { it('should validate international phone number format', async () => { const userSchema = new Schema({ phone: { type: 'string', validate: { pattern: /^\+?[1-9]\d{1,14}$/, // E.164 format message: 'Phone number must be in international format' } } }); const users = db.collection('phoneUsers', { schema: userSchema }); // Valid phone numbers const validPhones = [ '+12025550179', '+442071234567', '+61491570156', '12025550179' ]; for (const phone of validPhones) { const user = await users.insertOne({ phone }); expect(user).to.have.property('_id'); expect(user.phone).to.equal(phone); } // Invalid phone numbers const invalidPhones = [ '+0123456789', // Starts with +0 'abc12345678', '+123-456-7890', // Contains non-digit characters '+', '' ]; for (const phone of invalidPhones) { try { await users.insertOne({ phone }); expect.fail(`Should have rejected invalid phone: ${phone}`); } catch (error) { expect(error.message).to.include('Phone number must be in international format'); } } }); }); describe('Username Validation', () => { it('should validate username format', async () => { const userSchema = new Schema({ username: { type: 'string', required: true, validate: { pattern: /^[a-z0-9_-]{3,16}$/, message: 'Username must be 3-16 characters and contain only letters, numbers, underscores or hyphens' } } }); const users = db.collection('usernameUsers', { schema: userSchema }); // Valid usernames const validUsernames = [ 'user123', 'john_doe', 'test-user', 'abc', 'a-very-long-name' ]; for (const username of validUsernames) { const user = await users.insertOne({ username }); expect(user).to.have.property('_id'); expect(user.username).to.equal(username); } // Invalid usernames const invalidUsernames = [ 'ab', // Too short 'A-uppercase-letter', // Contains uppercase 'user@name', // Contains special character 'a_very_long_username_that_exceeds_limit', // Too long '' ]; for (const username of invalidUsernames) { try { await users.insertOne({ username }); expect.fail(`Should have rejected invalid username: ${username}`); } catch (error) { expect(error.message).to.include('Username must be 3-16 characters'); } } }); }); describe('Product Code Validation', () => { it('should validate product code format', async () => { const productSchema = new Schema({ productCode: { type: 'string', validate: { pattern: /^[A-Z]{3}-\d{5}$/, message: 'Product code must be in format: ABC-12345' } } }); const products = db.collection('products', { schema: productSchema }); // Valid product codes const validCodes = [ 'ABC-12345', 'XYZ-00001', 'DEF-99999' ]; for (const productCode of validCodes) { const product = await products.insertOne({ productCode }); expect(product).to.have.property('_id'); expect(product.productCode).to.equal(productCode); } // Invalid product codes const invalidCodes = [ 'AB-12345', // Only 2 letters 'ABCD-12345', // 4 letters 'ABC-1234', // Only 4 digits 'ABC-123456', // 6 digits 'abc-12345', // Lowercase letters 'ABC12345', // Missing hyphen '' ]; for (const productCode of invalidCodes) { try { await products.insertOne({ productCode }); expect.fail(`Should have rejected invalid product code: ${productCode}`); } catch (error) { expect(error.message).to.include('Product code must be in format'); } } }); }); describe('Combined Pattern Validation', () => { it('should validate URL with additional length constraints', async () => { const urlSchema = new Schema({ url: { type: 'string', validate: { pattern: /^https?:\/\/[\w\.-]+\.[a-z]{2,}\/?.*$/, minLength: 10, maxLength: 2048, message: 'Invalid URL format' } } }); const sites = db.collection('sites', { schema: urlSchema }); // Valid URLs const validUrls = [ 'https://example.com', 'http://subdomain.example.co.uk/path', 'https://example.com/path?query=string' ]; for (const url of validUrls) { const site = await sites.insertOne({ url }); expect(site).to.have.property('_id'); expect(site.url).to.equal(url); } // Invalid URLs const invalidUrls = [ 'example.com', // Missing protocol 'https://', // Missing domain 'https://a.b', // Domain too short 'ftp://example.com', // Wrong protocol 'http://' + 'a'.repeat(2050) + '.com' // Too long ]; for (const url of invalidUrls) { try { await sites.insertOne({ url }); expect.fail(`Should have rejected invalid URL: ${url}`); } catch (error) { expect(error.message).to.include('Invalid URL format'); } } }); }); describe('Conditional Validation', () => { it('should validate zip codes based on country', async () => { const addressSchema = new Schema({ country: { type: 'string', required: true }, zipCode: { type: 'string', validate: { custom: (value, doc) => { if (doc.country === 'US') { return /^\d{5}(-\d{4})?$/.test(value) || 'Invalid US zip code'; } else if (doc.country === 'CA') { return /^[A-Za-z]\d[A-Za-z] ?\d[A-Za-z]\d$/.test(value) || 'Invalid Canadian postal code'; } return true; } } } }); const addresses = db.collection('addresses', { schema: addressSchema }); // Valid addresses const validAddresses = [ { country: 'US', zipCode: '12345' }, { country: 'US', zipCode: '12345-6789' }, { country: 'CA', zipCode: 'A1B2C3' }, { country: 'CA', zipCode: 'A1B 2C3' }, { country: 'UK', zipCode: 'ANY FORMAT' } // Not validated for UK ]; for (const address of validAddresses) { const result = await addresses.insertOne(address); expect(result).to.have.property('_id'); expect(result.country).to.equal(address.country); expect(result.zipCode).to.equal(address.zipCode); } // Invalid addresses const invalidAddresses = [ { country: 'US', zipCode: '1234' }, // Too short { country: 'US', zipCode: '123456' }, // Too long { country: 'US', zipCode: 'ABCDE' }, // Not numeric { country: 'CA', zipCode: '123456' }, // Wrong format { country: 'CA', zipCode: 'ABCDEF' } // Wrong format ]; for (const address of invalidAddresses) { try { await addresses.insertOne(address); expect.fail(`Should have rejected invalid address: ${JSON.stringify(address)}`); } catch (error) { if (address.country === 'US') { expect(error.message).to.include('Invalid US zip code'); } else if (address.country === 'CA') { expect(error.message).to.include('Invalid Canadian postal code'); } } } }); }); }); //# sourceMappingURL=pattern_validation.test.js.map