79 lines
3.9 KiB
PHP
79 lines
3.9 KiB
PHP
<?php
|
|
|
|
it('defines durable backup schema tables for records, components, jobs, and restore audit', function (): void {
|
|
$content = file_get_contents(app_path('classes/backup_schema_bootstrap.php'));
|
|
|
|
expect($content)->toContain('CREATE TABLE IF NOT EXISTS backup_records')
|
|
->and($content)->toContain('CREATE TABLE IF NOT EXISTS backup_components')
|
|
->and($content)->toContain('CREATE TABLE IF NOT EXISTS backup_jobs')
|
|
->and($content)->toContain('CREATE TABLE IF NOT EXISTS backup_restore_audit')
|
|
->and($content)->toContain('manifest_sha256')
|
|
->and($content)->toContain('encryption_key_id');
|
|
});
|
|
|
|
it('hardens database dumps by keeping passwords out of the mysqldump command', function (): void {
|
|
$content = file_get_contents(app_path('classes/db.php'));
|
|
|
|
expect($content)->toContain('MYSQL_PWD')
|
|
->and($content)->toContain('--single-transaction')
|
|
->and($content)->toContain('--routines')
|
|
->and($content)->toContain('--triggers')
|
|
->and($content)->toContain('--events')
|
|
->and($content)->not->toContain('--password=$pass');
|
|
});
|
|
|
|
it('uses encrypted component backups and never stores raw environment dumps', function (): void {
|
|
$content = file_get_contents(app_path('classes/backup_store.php'));
|
|
|
|
expect($content)->toContain('AES-256-GCM')
|
|
->and($content)->toContain('BACKUP_ENCRYPTION_KEY_V1')
|
|
->and($content)->toContain('required_runtime_config_keys')
|
|
->and($content)->toContain('createObjectBucketComponent')
|
|
->and($content)->not->toContain('json_encode($_ENV)')
|
|
->and($content)->not->toContain("exec('zip -r");
|
|
});
|
|
|
|
it('normalizes string boolean config values before destructive restore gates', function (): void {
|
|
$content = file_get_contents(app_path('classes/backup_store.php'));
|
|
|
|
expect($content)->toContain("['1', 'true', 'yes', 'on']")
|
|
->and($content)->toContain("['0', 'false', 'no', 'off', '']")
|
|
->and($content)->toContain('Backup system is disabled in backup configuration.')
|
|
->and($content)->toContain('Direct production restore is disabled in backup configuration.');
|
|
});
|
|
|
|
it('wires backup job, verification, restore preview, restore, and audit routes', function (): void {
|
|
$content = file_get_contents(app_path('routes/moduleBackupsRoute.php'));
|
|
|
|
expect($content)->toContain('/modules/backup/jobs/{id}')
|
|
->and($content)->toContain('/modules/backup/backups/{backup_uuid}/verify')
|
|
->and($content)->toContain('/modules/backup/backups/{backup_uuid}/restore/preview')
|
|
->and($content)->toContain('/modules/backup/backups/{backup_uuid}/restore')
|
|
->and($content)->toContain('/modules/backup/restore-audit')
|
|
->and($content)->toContain('modules_backup_restore')
|
|
->and($content)->toContain('Subuser sessions cannot manage backup disaster recovery.');
|
|
});
|
|
|
|
it('registers hourly backup enqueue, worker, and retention prune cron tasks', function (): void {
|
|
$content = file_get_contents(app_path('modules/backups/cron/tasks.php'));
|
|
$cron = file_get_contents(app_path('cron/Cron.php'));
|
|
|
|
expect($content)->toContain("'seconds' => 3600")
|
|
->and($content)->toContain('backups.process_jobs')
|
|
->and($content)->toContain('backups.prune_retention')
|
|
->and($cron)->toContain('processBackupJobs')
|
|
->and($cron)->toContain('pruneBackupRetention');
|
|
});
|
|
|
|
it('documents backup disaster recovery APIs and config variables in openapi', function (): void {
|
|
$content = file_get_contents(app_path('openapi.yaml'));
|
|
|
|
expect($content)->toContain('/modules/backup/jobs/{id}:')
|
|
->and($content)->toContain('/modules/backup/backups/{backup_uuid}/restore/preview:')
|
|
->and($content)->toContain('BackupRestoreRequest')
|
|
->and($content)->toContain('BackupRestoreAuditListResponse')
|
|
->and($content)->toContain('retention_recent_hours')
|
|
->and($content)->toContain('verification_required')
|
|
->and($content)->toContain('restore_enabled');
|
|
});
|