- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
249 lines
9.8 KiB
PHP
249 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/SchemaCompiler.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/Blueprint.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/ColumnDefinition.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/IndexDefinition.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/IndexType.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/ForeignKeyDefinition.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/ForeignKeyAction.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/PostgreSQLSchemaCompiler.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/Commands/CreateTableCommand.php';
|
|
require_once __DIR__ . '/../../src/Framework/Database/Schema/Commands/DropTableCommand.php';
|
|
|
|
use App\Framework\Database\Schema\Blueprint;
|
|
use App\Framework\Database\Schema\PostgreSQLSchemaCompiler;
|
|
use App\Framework\Database\Schema\Commands\CreateTableCommand;
|
|
|
|
echo "Testing PostgreSQL Range Types\n";
|
|
echo "===============================\n\n";
|
|
|
|
try {
|
|
$pdo = new PDO(
|
|
'pgsql:host=db;dbname=michaelschiemer',
|
|
'postgres',
|
|
'StartSimple2024!'
|
|
);
|
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
echo "✅ Database connection established\n\n";
|
|
|
|
$compiler = new PostgreSQLSchemaCompiler();
|
|
|
|
// Cleanup
|
|
echo "Cleanup...\n";
|
|
$pdo->exec("DROP TABLE IF EXISTS test_ranges CASCADE");
|
|
echo "✅ Cleanup complete\n\n";
|
|
|
|
// Test 1: Create table with all range types
|
|
echo "Test 1: Create Table With Range Types\n";
|
|
echo "======================================\n";
|
|
|
|
$blueprint = new Blueprint('test_ranges');
|
|
$blueprint->bigIncrements('id');
|
|
$blueprint->string('description');
|
|
|
|
// Numeric ranges
|
|
$blueprint->int4range('age_range')->nullable();
|
|
$blueprint->int8range('population_range')->nullable();
|
|
$blueprint->numrange('price_range')->nullable();
|
|
|
|
// Timestamp ranges
|
|
$blueprint->tsrange('event_period')->nullable();
|
|
$blueprint->tstzrange('booking_period')->nullable();
|
|
|
|
// Date ranges
|
|
$blueprint->daterange('valid_dates')->nullable();
|
|
|
|
$createCommand = new CreateTableCommand('test_ranges', $blueprint);
|
|
$sql = $compiler->compile($createCommand);
|
|
|
|
echo "Creating table with range columns:\n";
|
|
echo $sql . "\n\n";
|
|
|
|
$pdo->exec($sql);
|
|
echo "✅ Table 'test_ranges' created with all range types\n\n";
|
|
|
|
// Test 2: Insert data with range values
|
|
echo "Test 2: Insert Range Data\n";
|
|
echo "==========================\n";
|
|
|
|
$pdo->exec("
|
|
INSERT INTO test_ranges (
|
|
description,
|
|
age_range,
|
|
population_range,
|
|
price_range,
|
|
event_period,
|
|
booking_period,
|
|
valid_dates
|
|
) VALUES (
|
|
'Hotel Booking',
|
|
'[18,65)', -- Age 18 to 64 (inclusive start, exclusive end)
|
|
'[1000000,5000000)', -- Population range
|
|
'[99.99,499.99]', -- Price range (both inclusive)
|
|
'[2024-06-01 10:00:00, 2024-06-01 18:00:00)', -- Event period
|
|
'[2024-06-01 10:00:00+02, 2024-06-01 18:00:00+02)', -- Booking with timezone
|
|
'[2024-01-01, 2024-12-31]' -- Valid date range
|
|
)
|
|
");
|
|
echo "✅ Range data inserted\n\n";
|
|
|
|
// Test 3: Query range data
|
|
echo "Test 3: Query Range Data\n";
|
|
echo "=========================\n";
|
|
|
|
$stmt = $pdo->query("SELECT * FROM test_ranges WHERE id = 1");
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
echo "Retrieved data:\n";
|
|
echo " - Description: {$result['description']}\n";
|
|
echo " - Age Range: {$result['age_range']}\n";
|
|
echo " - Population Range: {$result['population_range']}\n";
|
|
echo " - Price Range: {$result['price_range']}\n";
|
|
echo " - Event Period: {$result['event_period']}\n";
|
|
echo " - Booking Period: {$result['booking_period']}\n";
|
|
echo " - Valid Dates: {$result['valid_dates']}\n";
|
|
echo "✅ Range data successfully retrieved\n\n";
|
|
|
|
// Test 4: Range operators
|
|
echo "Test 4: Range Operators\n";
|
|
echo "========================\n";
|
|
|
|
// Insert test data for operator tests
|
|
$pdo->exec("
|
|
INSERT INTO test_ranges (description, age_range, price_range, valid_dates)
|
|
VALUES
|
|
('Product A', '[25,50)', '[100.00,200.00]', '[2024-01-01,2024-06-30]'),
|
|
('Product B', '[30,60)', '[150.00,300.00]', '[2024-03-01,2024-09-30]'),
|
|
('Product C', '[40,70)', '[250.00,500.00]', '[2024-06-01,2024-12-31]')
|
|
");
|
|
echo "✅ Test data inserted\n\n";
|
|
|
|
// Test @> (contains) operator
|
|
echo "Test @> (contains) operator:\n";
|
|
$stmt = $pdo->query("SELECT description FROM test_ranges WHERE age_range @> 35");
|
|
$results = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
echo " Products with age range containing 35: " . implode(', ', $results) . "\n";
|
|
echo (count($results) == 3) ? "✅ Contains operator works\n\n" : "❌ Should find 3 products\n\n";
|
|
|
|
// Test <@ (is contained by) operator
|
|
echo "Test <@ (is contained by) operator:\n";
|
|
$stmt = $pdo->query("SELECT description FROM test_ranges WHERE '[100,150]'::numrange <@ price_range");
|
|
$results = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
echo " Products containing price range [100,150]: " . implode(', ', $results) . "\n";
|
|
echo (count($results) >= 1) ? "✅ Is contained by operator works\n\n" : "❌ Should find products\n\n";
|
|
|
|
// Test && (overlap) operator
|
|
echo "Test && (overlap) operator:\n";
|
|
$stmt = $pdo->query("SELECT description FROM test_ranges WHERE valid_dates && '[2024-05-01,2024-07-31]'::daterange");
|
|
$results = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
echo " Products with overlapping date range: " . implode(', ', $results) . "\n";
|
|
echo (count($results) >= 2) ? "✅ Overlap operator works\n\n" : "❌ Should find overlapping products\n\n";
|
|
|
|
// Test 5: Range functions
|
|
echo "Test 5: Range Functions\n";
|
|
echo "========================\n";
|
|
|
|
// lower() and upper() functions
|
|
$stmt = $pdo->query("
|
|
SELECT
|
|
description,
|
|
lower(age_range) as min_age,
|
|
upper(age_range) as max_age,
|
|
lower(price_range) as min_price,
|
|
upper(price_range) as max_price
|
|
FROM test_ranges
|
|
WHERE description = 'Product A'
|
|
");
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
echo "Product A bounds:\n";
|
|
echo " - Min Age: {$result['min_age']}\n";
|
|
echo " - Max Age: {$result['max_age']}\n";
|
|
echo " - Min Price: {$result['min_price']}\n";
|
|
echo " - Max Price: {$result['max_price']}\n";
|
|
echo "✅ Range boundary functions work\n\n";
|
|
|
|
// isempty() function
|
|
echo "Test isempty() function:\n";
|
|
$stmt = $pdo->query("SELECT COUNT(*) FROM test_ranges WHERE isempty(age_range)");
|
|
$emptyCount = $stmt->fetchColumn();
|
|
echo " - Empty ranges: {$emptyCount}\n";
|
|
echo ($emptyCount == 0) ? "✅ No empty ranges found (as expected)\n\n" : "✅ isempty() function works\n\n";
|
|
|
|
// Test 6: Range intersection
|
|
echo "Test 6: Range Intersection\n";
|
|
echo "===========================\n";
|
|
|
|
// Test range intersection
|
|
$stmt = $pdo->query("
|
|
SELECT
|
|
'[25,60)'::int4range * '[30,50)'::int4range as intersection,
|
|
'[100.00,200.00]'::numrange * '[150.00,300.00]'::numrange as price_intersection
|
|
");
|
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
echo "Range intersections:\n";
|
|
echo " - Age range [25,60) * [30,50) = {$result['intersection']}\n";
|
|
echo " - Price range [100,200] * [150,300] = {$result['price_intersection']}\n";
|
|
echo "✅ Range intersection operator works\n\n";
|
|
|
|
// Test 7: GiST index on range column
|
|
echo "Test 7: GiST Index on Range Column\n";
|
|
echo "===================================\n";
|
|
|
|
$pdo->exec("CREATE INDEX idx_test_ranges_age ON test_ranges USING GIST (age_range)");
|
|
echo "✅ GiST index created on age_range column\n";
|
|
|
|
$pdo->exec("CREATE INDEX idx_test_ranges_price ON test_ranges USING GIST (price_range)");
|
|
echo "✅ GiST index created on price_range column\n";
|
|
|
|
$pdo->exec("CREATE INDEX idx_test_ranges_dates ON test_ranges USING GIST (valid_dates)");
|
|
echo "✅ GiST index created on valid_dates column\n\n";
|
|
|
|
// Test 8: Verify indexes are used
|
|
echo "Test 8: Verify Index Usage\n";
|
|
echo "===========================\n";
|
|
|
|
$stmt = $pdo->query("
|
|
SELECT indexname, indexdef
|
|
FROM pg_indexes
|
|
WHERE tablename = 'test_ranges'
|
|
AND indexname LIKE 'idx_test_ranges_%'
|
|
ORDER BY indexname
|
|
");
|
|
$indexes = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
echo "Found " . count($indexes) . " GiST indexes:\n";
|
|
foreach ($indexes as $index) {
|
|
echo " - {$index['indexname']}\n";
|
|
}
|
|
echo (count($indexes) == 3) ? "✅ All GiST indexes created\n\n" : "❌ Expected 3 indexes\n\n";
|
|
|
|
// Cleanup
|
|
echo "Cleanup...\n";
|
|
$pdo->exec("DROP TABLE IF EXISTS test_ranges CASCADE");
|
|
echo "✅ Test data cleaned up\n";
|
|
|
|
echo "\n✅ All Range Types tests passed!\n";
|
|
echo "\nSummary:\n";
|
|
echo "========\n";
|
|
echo "✅ INT4RANGE (integer ranges) works\n";
|
|
echo "✅ INT8RANGE (bigint ranges) works\n";
|
|
echo "✅ NUMRANGE (numeric ranges) works\n";
|
|
echo "✅ TSRANGE (timestamp ranges) works\n";
|
|
echo "✅ TSTZRANGE (timestamp with timezone ranges) works\n";
|
|
echo "✅ DATERANGE (date ranges) works\n";
|
|
echo "✅ Range operators (@>, <@, &&) work\n";
|
|
echo "✅ Range functions (lower, upper, isempty) work\n";
|
|
echo "✅ Range intersection operator (*) works\n";
|
|
echo "✅ GiST indexes on range columns work\n";
|
|
|
|
} catch (\Exception $e) {
|
|
echo "❌ Error: " . $e->getMessage() . "\n";
|
|
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
|
|
exit(1);
|
|
}
|