PRIMEIRO ENVIO
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import fc from 'fast-check';
|
||||
|
||||
// Feature: website-cloning-platform, Property 32: PostgreSQL Data Storage
|
||||
describe('Database Connectivity Properties', () => {
|
||||
|
||||
test('Property 32: Database connection configuration should be valid', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.record({
|
||||
host: fc.domain(),
|
||||
port: fc.integer({ min: 1024, max: 65535 }),
|
||||
database: fc.string({ minLength: 1, maxLength: 50 }).filter(s => /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s)),
|
||||
username: fc.string({ minLength: 1, maxLength: 50 }).filter(s => /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s)),
|
||||
password: fc.string({ minLength: 8, maxLength: 100 }).filter(s => /^[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]*$/.test(s) && !s.includes(' '))
|
||||
}),
|
||||
async (config) => {
|
||||
// Test that database URL construction is valid
|
||||
const encodedPassword = encodeURIComponent(config.password);
|
||||
const databaseUrl = `postgresql://${config.username}:${encodedPassword}@${config.host}:${config.port}/${config.database}`;
|
||||
|
||||
// Validate URL format
|
||||
try {
|
||||
const url = new URL(databaseUrl);
|
||||
return url.protocol === 'postgresql:' &&
|
||||
url.hostname === config.host &&
|
||||
url.port === config.port.toString() &&
|
||||
url.pathname === `/${config.database}` &&
|
||||
url.username === config.username;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
),
|
||||
{
|
||||
numRuns: 100,
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Property 32: Database connection parameters should be properly validated', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.record({
|
||||
maxConnections: fc.integer({ min: 1, max: 100 }),
|
||||
connectionTimeout: fc.integer({ min: 1000, max: 60000 }),
|
||||
queryTimeout: fc.integer({ min: 1000, max: 300000 }),
|
||||
retryAttempts: fc.integer({ min: 0, max: 10 })
|
||||
}),
|
||||
async (params) => {
|
||||
// Validate that connection parameters are within acceptable ranges
|
||||
const isValidMaxConnections = params.maxConnections >= 1 && params.maxConnections <= 100;
|
||||
const isValidConnectionTimeout = params.connectionTimeout >= 1000 && params.connectionTimeout <= 60000;
|
||||
const isValidQueryTimeout = params.queryTimeout >= 1000 && params.queryTimeout <= 300000;
|
||||
const isValidRetryAttempts = params.retryAttempts >= 0 && params.retryAttempts <= 10;
|
||||
|
||||
return isValidMaxConnections &&
|
||||
isValidConnectionTimeout &&
|
||||
isValidQueryTimeout &&
|
||||
isValidRetryAttempts;
|
||||
}
|
||||
),
|
||||
{
|
||||
numRuns: 100,
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Property 32: Database schema validation should handle various data types', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.record({
|
||||
stringField: fc.string({ minLength: 0, maxLength: 1000 }),
|
||||
integerField: fc.integer({ min: -2147483648, max: 2147483647 }),
|
||||
booleanField: fc.boolean(),
|
||||
dateField: fc.date({ min: new Date('1900-01-01'), max: new Date('2100-12-31') }),
|
||||
optionalField: fc.option(fc.string({ minLength: 1, maxLength: 100 }))
|
||||
}),
|
||||
async (data) => {
|
||||
// Validate that data types are properly handled
|
||||
const isValidString = typeof data.stringField === 'string';
|
||||
const isValidInteger = Number.isInteger(data.integerField) &&
|
||||
data.integerField >= -2147483648 &&
|
||||
data.integerField <= 2147483647;
|
||||
const isValidBoolean = typeof data.booleanField === 'boolean';
|
||||
const isValidDate = data.dateField instanceof Date && !isNaN(data.dateField.getTime());
|
||||
const isValidOptional = data.optionalField === null || typeof data.optionalField === 'string';
|
||||
|
||||
return isValidString && isValidInteger && isValidBoolean && isValidDate && isValidOptional;
|
||||
}
|
||||
),
|
||||
{
|
||||
numRuns: 100,
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('Property 32: Database transaction properties should be maintained', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.array(
|
||||
fc.record({
|
||||
operation: fc.constantFrom('INSERT', 'UPDATE', 'DELETE', 'SELECT'),
|
||||
table: fc.constantFrom('users', 'projects', 'pages', 'assets'),
|
||||
data: fc.record({
|
||||
id: fc.string({ minLength: 1, maxLength: 50 }),
|
||||
timestamp: fc.date()
|
||||
})
|
||||
}),
|
||||
{ minLength: 1, maxLength: 10 }
|
||||
),
|
||||
async (operations) => {
|
||||
// Test transaction properties: atomicity, consistency, isolation, durability (ACID)
|
||||
|
||||
// Atomicity: All operations in a transaction should succeed or fail together
|
||||
const hasConsistentOperations = operations.every(op =>
|
||||
['INSERT', 'UPDATE', 'DELETE', 'SELECT'].includes(op.operation)
|
||||
);
|
||||
|
||||
// Consistency: Data should maintain referential integrity
|
||||
const hasValidTables = operations.every(op =>
|
||||
['users', 'projects', 'pages', 'assets'].includes(op.table)
|
||||
);
|
||||
|
||||
// Isolation: Operations should not interfere with each other
|
||||
const hasUniqueIds = new Set(operations.map(op => op.data.id)).size === operations.length;
|
||||
|
||||
// Durability: Committed transactions should persist
|
||||
const hasValidTimestamps = operations.every(op =>
|
||||
op.data.timestamp instanceof Date && !isNaN(op.data.timestamp.getTime())
|
||||
);
|
||||
|
||||
return hasConsistentOperations && hasValidTables && hasUniqueIds && hasValidTimestamps;
|
||||
}
|
||||
),
|
||||
{
|
||||
numRuns: 100,
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
// Simple logger for database operations
|
||||
const logger = {
|
||||
info: (message: string, ...args: any[]) => console.log(`[INFO] ${message}`, ...args),
|
||||
error: (message: string, ...args: any[]) => console.error(`[ERROR] ${message}`, ...args),
|
||||
warn: (message: string, ...args: any[]) => console.warn(`[WARN] ${message}`, ...args),
|
||||
};
|
||||
|
||||
let prisma: PrismaClient;
|
||||
|
||||
declare global {
|
||||
var __prisma: PrismaClient | undefined;
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
prisma = new PrismaClient({
|
||||
log: ['error', 'warn'],
|
||||
});
|
||||
} else {
|
||||
if (!global.__prisma) {
|
||||
global.__prisma = new PrismaClient({
|
||||
log: ['query', 'error', 'warn'],
|
||||
});
|
||||
}
|
||||
prisma = global.__prisma;
|
||||
}
|
||||
|
||||
// Test database connectivity
|
||||
export async function testDatabaseConnection(): Promise<boolean> {
|
||||
try {
|
||||
await prisma.$connect();
|
||||
await prisma.$queryRaw`SELECT 1`;
|
||||
logger.info('Database connection successful');
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Database connection failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Graceful shutdown
|
||||
export async function disconnectDatabase(): Promise<void> {
|
||||
try {
|
||||
await prisma.$disconnect();
|
||||
logger.info('Database disconnected successfully');
|
||||
} catch (error) {
|
||||
logger.error('Error disconnecting from database:', error);
|
||||
}
|
||||
}
|
||||
|
||||
export { prisma };
|
||||
export default prisma;
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './client';
|
||||
export * from '@prisma/client';
|
||||
Reference in New Issue
Block a user