first commit
This commit is contained in:
commit
d5c8076e2f
3 changed files with 471 additions and 0 deletions
122
WISSKI-UPDATE-TESTING.md
Normal file
122
WISSKI-UPDATE-TESTING.md
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
# WissKI Table Update Data Integrity Testing
|
||||||
|
|
||||||
|
This directory contains scripts to test data integrity when updating WissKI tables. The scripts ensure that no data is lost or truncated during schema updates.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `test-wisski-data-integrity.php` - PHP script to export and verify data
|
||||||
|
- `test-wisski-update.sh` - Bash wrapper script for the complete testing process
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Option 1: Automated Testing (Recommended)
|
||||||
|
|
||||||
|
Run the complete test process (export → update → verify):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./test-wisski-update.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will:
|
||||||
|
1. Export all WissKI table data (row counts, checksums, sample rows)
|
||||||
|
2. Prompt you to run Drupal updates
|
||||||
|
3. Verify data integrity after updates
|
||||||
|
4. Generate a detailed report
|
||||||
|
|
||||||
|
### Option 2: Manual Step-by-Step
|
||||||
|
|
||||||
|
#### Step 1: Export Data Before Update
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php test-wisski-data-integrity.php export
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a directory `wisski-data-integrity-test/` with:
|
||||||
|
- `before-update-summary.json` - Summary of all tables
|
||||||
|
- Individual table JSON files with row counts, checksums, and sample data
|
||||||
|
|
||||||
|
#### Step 2: Run Drupal Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
drush updatedb -y
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use the Drupal admin interface: `/update.php`
|
||||||
|
|
||||||
|
#### Step 3: Verify Data Integrity
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php test-wisski-data-integrity.php verify
|
||||||
|
```
|
||||||
|
|
||||||
|
This compares the current state with the exported data and reports:
|
||||||
|
- Row count changes
|
||||||
|
- Checksum mismatches (data changes)
|
||||||
|
- Sample data differences
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
All test results are saved in `wisski-data-integrity-test/`:
|
||||||
|
|
||||||
|
- `before-update-summary.json` - Complete export before update
|
||||||
|
- `verification-report.json` - Detailed verification results
|
||||||
|
- Individual table JSON files for detailed inspection
|
||||||
|
|
||||||
|
## What Gets Tested
|
||||||
|
|
||||||
|
For each WissKI table (all tables with `wisski` prefix):
|
||||||
|
|
||||||
|
1. **Row Count** - Ensures no rows are lost or added unexpectedly
|
||||||
|
2. **Data Checksum** - MD5 hash of all table data to detect any changes
|
||||||
|
3. **Sample Rows** - First 10 rows compared to detect data corruption
|
||||||
|
4. **Column Information** - Schema changes are logged
|
||||||
|
|
||||||
|
## Interpreting Results
|
||||||
|
|
||||||
|
### ✓ Success
|
||||||
|
- All row counts match
|
||||||
|
- All checksums match
|
||||||
|
- No data loss detected
|
||||||
|
|
||||||
|
### ⚠️ Issues Detected
|
||||||
|
- **Row count mismatch**: Rows were added or deleted
|
||||||
|
- **Checksum mismatch**: Data content changed (may be expected for schema updates)
|
||||||
|
- **Sample data mismatch**: First rows differ (investigate for corruption)
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. **Backup First**: Always backup your database before running updates
|
||||||
|
2. **Schema Changes**: Some checksum changes may be expected if column types change (e.g., VARCHAR to TEXT)
|
||||||
|
3. **Large Tables**: For very large tables, checksum calculation may fall back to partial checksums
|
||||||
|
4. **Empty Tables**: Empty tables are marked with checksum 'empty' and skipped in checksum comparison
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If verification fails:
|
||||||
|
|
||||||
|
1. Check `verification-report.json` for detailed information
|
||||||
|
2. Compare individual table JSON files before/after
|
||||||
|
3. Review Drupal update logs for errors
|
||||||
|
4. Check if schema changes are expected (column type changes, etc.)
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```
|
||||||
|
Exporting data for 450 WissKI tables...
|
||||||
|
Processing wisski_entity_map...
|
||||||
|
Processing wisski_calling_bundles...
|
||||||
|
...
|
||||||
|
|
||||||
|
✓ Data export complete.
|
||||||
|
|
||||||
|
Verifying data integrity for 450 tables...
|
||||||
|
Checking wisski_entity_map...
|
||||||
|
✓ Row count matches: 1234
|
||||||
|
✓ Checksum matches
|
||||||
|
✓ Sample data matches
|
||||||
|
|
||||||
|
==========================================
|
||||||
|
✓ SUCCESS: All data verified successfully!
|
||||||
|
==========================================
|
||||||
|
```
|
||||||
|
|
||||||
274
test-wisski-data-integrity.php
Normal file
274
test-wisski-data-integrity.php
Normal file
|
|
@ -0,0 +1,274 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Script to test WissKI table data integrity before and after schema updates.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* php test-wisski-data-integrity.php export # Export data before update
|
||||||
|
* php test-wisski-data-integrity.php verify # Verify data after update
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Bootstrap Drupal.
|
||||||
|
$autoloader = require_once __DIR__ . '/autoload.php';
|
||||||
|
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
|
||||||
|
$kernel = \Drupal\Core\DrupalKernel::createFromRequest($request, $autoloader, 'prod');
|
||||||
|
$kernel->boot();
|
||||||
|
|
||||||
|
// Database connection.
|
||||||
|
$connection = \Drupal::database();
|
||||||
|
$outputDir = __DIR__ . '/wisski-data-integrity-test';
|
||||||
|
|
||||||
|
// Get all WissKI tables.
|
||||||
|
function getWisskiTables($connection) {
|
||||||
|
$tables = [];
|
||||||
|
// Query information_schema to get all WissKI tables.
|
||||||
|
$database = $connection->getConnectionOptions();
|
||||||
|
$dbName = $database['database'];
|
||||||
|
$result = $connection->query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = :db AND TABLE_NAME LIKE 'wisski%'", [
|
||||||
|
':db' => $dbName,
|
||||||
|
]);
|
||||||
|
while ($row = $result->fetchAssoc()) {
|
||||||
|
$tables[] = $row['TABLE_NAME'];
|
||||||
|
}
|
||||||
|
return $tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export table data: row count, checksum, sample rows.
|
||||||
|
function exportTableData($connection, $tableName, $outputDir) {
|
||||||
|
$data = [
|
||||||
|
'table' => $tableName,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'row_count' => 0,
|
||||||
|
'checksum' => '',
|
||||||
|
'sample_rows' => [],
|
||||||
|
'column_info' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get row count.
|
||||||
|
$countResult = $connection->query("SELECT COUNT(*) as cnt FROM {" . $tableName . "}");
|
||||||
|
$data['row_count'] = (int) $countResult->fetchField();
|
||||||
|
|
||||||
|
if ($data['row_count'] === 0) {
|
||||||
|
$data['checksum'] = 'empty';
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get column information.
|
||||||
|
$columnsResult = $connection->query("SHOW COLUMNS FROM {" . $tableName . "}");
|
||||||
|
$columns = [];
|
||||||
|
while ($col = $columnsResult->fetchAssoc()) {
|
||||||
|
$columns[] = $col['Field'];
|
||||||
|
}
|
||||||
|
$data['column_info'] = $columns;
|
||||||
|
|
||||||
|
// Get sample rows (first 10 rows).
|
||||||
|
$sampleResult = $connection->query("SELECT * FROM {" . $tableName . "} LIMIT 10");
|
||||||
|
$samples = [];
|
||||||
|
while ($row = $sampleResult->fetchAssoc()) {
|
||||||
|
$samples[] = $row;
|
||||||
|
}
|
||||||
|
$data['sample_rows'] = $samples;
|
||||||
|
|
||||||
|
// Calculate checksum of all data.
|
||||||
|
// For large tables, we'll use a hash of all row data concatenated.
|
||||||
|
// Build checksum query - use backticks for column names.
|
||||||
|
$checksumFields = [];
|
||||||
|
foreach ($columns as $col) {
|
||||||
|
$checksumFields[] = "COALESCE(CAST(`" . $col . "` AS CHAR), '')";
|
||||||
|
}
|
||||||
|
$orderByCols = array_slice($columns, 0, min(5, count($columns)));
|
||||||
|
$orderBy = implode(', ', array_map(function($col) {
|
||||||
|
return "`" . $col . "`";
|
||||||
|
}, $orderByCols));
|
||||||
|
|
||||||
|
// Use CONCAT_WS for safer concatenation.
|
||||||
|
$concatExpr = "CONCAT_WS('|', " . implode(', ', $checksumFields) . ")";
|
||||||
|
$checksumQuery = "SELECT MD5(GROUP_CONCAT(" . $concatExpr .
|
||||||
|
" ORDER BY " . $orderBy . " SEPARATOR '|||')) as chksum FROM {" . $tableName . "}";
|
||||||
|
|
||||||
|
try {
|
||||||
|
$checksumResult = $connection->query($checksumQuery);
|
||||||
|
$data['checksum'] = $checksumResult->fetchField() ?: 'no_checksum';
|
||||||
|
}
|
||||||
|
catch (\Exception $e) {
|
||||||
|
// If checksum fails (e.g., GROUP_CONCAT too large), use row count + first row hash.
|
||||||
|
$data['checksum'] = 'partial_' . md5($data['row_count'] . serialize($samples[0] ?? []));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export all WissKI tables.
|
||||||
|
function exportAllTables($connection, $outputDir) {
|
||||||
|
if (!is_dir($outputDir)) {
|
||||||
|
mkdir($outputDir, 0755, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tables = getWisskiTables($connection);
|
||||||
|
$allData = [
|
||||||
|
'export_timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'tables' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
echo "Exporting data for " . count($tables) . " WissKI tables...\n";
|
||||||
|
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
echo " Processing {$table}...\n";
|
||||||
|
$tableData = exportTableData($connection, $table, $outputDir);
|
||||||
|
$allData['tables'][$table] = $tableData;
|
||||||
|
|
||||||
|
// Save individual table export.
|
||||||
|
$tableFile = $outputDir . '/' . $table . '.json';
|
||||||
|
file_put_contents($tableFile, json_encode($tableData, JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save summary.
|
||||||
|
$summaryFile = $outputDir . '/before-update-summary.json';
|
||||||
|
file_put_contents($summaryFile, json_encode($allData, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo "\nExport complete! Data saved to: {$outputDir}\n";
|
||||||
|
echo "Summary: {$summaryFile}\n";
|
||||||
|
|
||||||
|
return $allData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify data integrity after update.
|
||||||
|
function verifyDataIntegrity($connection, $outputDir) {
|
||||||
|
$summaryFile = $outputDir . '/before-update-summary.json';
|
||||||
|
|
||||||
|
if (!file_exists($summaryFile)) {
|
||||||
|
echo "ERROR: Before-update summary not found at: {$summaryFile}\n";
|
||||||
|
echo "Please run 'export' first.\n";
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$beforeData = json_decode(file_get_contents($summaryFile), TRUE);
|
||||||
|
$issues = [];
|
||||||
|
$verified = [];
|
||||||
|
|
||||||
|
echo "Verifying data integrity for " . count($beforeData['tables']) . " tables...\n\n";
|
||||||
|
|
||||||
|
foreach ($beforeData['tables'] as $tableName => $beforeTableData) {
|
||||||
|
echo "Checking {$tableName}...\n";
|
||||||
|
|
||||||
|
$afterTableData = exportTableData($connection, $tableName, $outputDir);
|
||||||
|
|
||||||
|
// Check row count.
|
||||||
|
if ($afterTableData['row_count'] !== $beforeTableData['row_count']) {
|
||||||
|
$issues[] = [
|
||||||
|
'table' => $tableName,
|
||||||
|
'issue' => 'row_count_mismatch',
|
||||||
|
'before' => $beforeTableData['row_count'],
|
||||||
|
'after' => $afterTableData['row_count'],
|
||||||
|
'difference' => $afterTableData['row_count'] - $beforeTableData['row_count'],
|
||||||
|
];
|
||||||
|
echo " ⚠️ ROW COUNT MISMATCH: Before={$beforeTableData['row_count']}, After={$afterTableData['row_count']}\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo " ✓ Row count matches: {$afterTableData['row_count']}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check checksum.
|
||||||
|
if ($beforeTableData['checksum'] !== 'empty' && $afterTableData['checksum'] !== 'empty') {
|
||||||
|
if ($beforeTableData['checksum'] !== $afterTableData['checksum']) {
|
||||||
|
$issues[] = [
|
||||||
|
'table' => $tableName,
|
||||||
|
'issue' => 'checksum_mismatch',
|
||||||
|
'before' => $beforeTableData['checksum'],
|
||||||
|
'after' => $afterTableData['checksum'],
|
||||||
|
];
|
||||||
|
echo " ⚠️ CHECKSUM MISMATCH: Data may have changed!\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo " ✓ Checksum matches\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare sample rows if available.
|
||||||
|
if (!empty($beforeTableData['sample_rows']) && !empty($afterTableData['sample_rows'])) {
|
||||||
|
$sampleMatch = TRUE;
|
||||||
|
$minSamples = min(count($beforeTableData['sample_rows']), count($afterTableData['sample_rows']));
|
||||||
|
for ($i = 0; $i < $minSamples; $i++) {
|
||||||
|
if ($beforeTableData['sample_rows'][$i] !== $afterTableData['sample_rows'][$i]) {
|
||||||
|
$sampleMatch = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$sampleMatch) {
|
||||||
|
$issues[] = [
|
||||||
|
'table' => $tableName,
|
||||||
|
'issue' => 'sample_data_mismatch',
|
||||||
|
];
|
||||||
|
echo " ⚠️ Sample data differs!\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo " ✓ Sample data matches\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty(array_filter($issues, function($issue) use ($tableName) {
|
||||||
|
return $issue['table'] === $tableName;
|
||||||
|
}))) {
|
||||||
|
$verified[] = $tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save verification report.
|
||||||
|
$report = [
|
||||||
|
'verification_timestamp' => date('Y-m-d H:i:s'),
|
||||||
|
'tables_verified' => count($verified),
|
||||||
|
'tables_with_issues' => count($issues),
|
||||||
|
'verified_tables' => $verified,
|
||||||
|
'issues' => $issues,
|
||||||
|
];
|
||||||
|
|
||||||
|
$reportFile = $outputDir . '/verification-report.json';
|
||||||
|
file_put_contents($reportFile, json_encode($report, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Print summary.
|
||||||
|
echo "\n" . str_repeat('=', 60) . "\n";
|
||||||
|
echo "VERIFICATION SUMMARY\n";
|
||||||
|
echo str_repeat('=', 60) . "\n";
|
||||||
|
echo "Tables verified: " . count($verified) . "\n";
|
||||||
|
echo "Tables with issues: " . count($issues) . "\n";
|
||||||
|
|
||||||
|
if (!empty($issues)) {
|
||||||
|
echo "\n⚠️ ISSUES FOUND:\n";
|
||||||
|
foreach ($issues as $issue) {
|
||||||
|
echo " - {$issue['table']}: {$issue['issue']}\n";
|
||||||
|
if (isset($issue['difference'])) {
|
||||||
|
echo " Difference: {$issue['difference']} rows\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "\nFull report: {$reportFile}\n";
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo "\n✓ All tables verified successfully! No data loss detected.\n";
|
||||||
|
echo "Report: {$reportFile}\n";
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main execution.
|
||||||
|
$command = $argv[1] ?? 'help';
|
||||||
|
|
||||||
|
switch ($command) {
|
||||||
|
case 'export':
|
||||||
|
exportAllTables($connection, $outputDir);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'verify':
|
||||||
|
verifyDataIntegrity($connection, $outputDir);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo "Usage:\n";
|
||||||
|
echo " php test-wisski-data-integrity.php export # Export data before update\n";
|
||||||
|
echo " php test-wisski-data-integrity.php verify # Verify data after update\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
75
test-wisski-update.sh
Executable file
75
test-wisski-update.sh
Executable file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to test WissKI table updates with data integrity checks.
|
||||||
|
# This script exports data before update, runs the update, and verifies data integrity.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/wisski-data-integrity-test"
|
||||||
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "WissKI Table Update Data Integrity Test"
|
||||||
|
echo "=========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 1: Export data before update.
|
||||||
|
echo "Step 1: Exporting data BEFORE update..."
|
||||||
|
echo "----------------------------------------"
|
||||||
|
php test-wisski-data-integrity.php export
|
||||||
|
|
||||||
|
if [ ! -f "$OUTPUT_DIR/before-update-summary.json" ]; then
|
||||||
|
echo "ERROR: Failed to export data before update!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Data export complete. Backup saved to: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter to continue with the update, or Ctrl+C to cancel..."
|
||||||
|
|
||||||
|
# Step 2: Run Drupal updates.
|
||||||
|
echo ""
|
||||||
|
echo "Step 2: Running Drupal updates..."
|
||||||
|
echo "----------------------------------------"
|
||||||
|
|
||||||
|
# Check if drush is available.
|
||||||
|
if command -v drush &> /dev/null; then
|
||||||
|
echo "Running: drush updatedb -y"
|
||||||
|
drush updatedb -y
|
||||||
|
else
|
||||||
|
echo "Drush not found. Please run Drupal updates manually:"
|
||||||
|
echo " drush updatedb -y"
|
||||||
|
echo ""
|
||||||
|
read -p "Press Enter after you have run the updates, or Ctrl+C to cancel..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: Verify data integrity.
|
||||||
|
echo ""
|
||||||
|
echo "Step 3: Verifying data integrity AFTER update..."
|
||||||
|
echo "----------------------------------------"
|
||||||
|
php test-wisski-data-integrity.php verify
|
||||||
|
|
||||||
|
VERIFICATION_EXIT=$?
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ $VERIFICATION_EXIT -eq 0 ]; then
|
||||||
|
echo "=========================================="
|
||||||
|
echo "✓ SUCCESS: All data verified successfully!"
|
||||||
|
echo "=========================================="
|
||||||
|
else
|
||||||
|
echo "=========================================="
|
||||||
|
echo "⚠️ WARNING: Issues detected during verification!"
|
||||||
|
echo "Check the verification report for details."
|
||||||
|
echo "=========================================="
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Test results saved to: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
exit $VERIFICATION_EXIT
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue