Implements Spec 087: Legacy Runs Removal (rigorous). ### What changed - Canonicalized run history: **`operation_runs` is the only run system** for inventory sync, Entra group sync, backup schedule execution/retention/purge. - Removed legacy UI surfaces (Filament Resources / relation managers) for legacy run models. - Legacy run URLs now return **404** (no redirects), with RBAC semantics preserved (404 vs 403 as specified). - Canonicalized affected `operation_runs.type` values (dotted → underscore) via migration. - Drift + inventory references now point to canonical operation runs; includes backfills and then drops legacy FK columns. - Drops legacy run tables after cutover. - Added regression guards to prevent reintroducing legacy run tokens or “backfilling” canonical runs from legacy tables. ### Migrations - `2026_02_12_000001..000006_*` canonicalize types, add/backfill operation_run_id references, drop legacy columns, and drop legacy run tables. ### Tests Focused pack for this spec passed: - `tests/Feature/Guards/NoLegacyRunsTest.php` - `tests/Feature/Guards/NoLegacyRunBackfillTest.php` - `tests/Feature/Operations/LegacyRunRoutesNotFoundTest.php` - `tests/Feature/Monitoring/MonitoringOperationsTest.php` - `tests/Feature/Jobs/RunInventorySyncJobTest.php` ### Notes / impact - Destructive cleanup is handled via migrations (drops legacy tables) after code cutover; deploy should run migrations in the same release. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #106
62 lines
2.0 KiB
PHP
62 lines
2.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Support\OperationCatalog;
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
it('fails fast if any code-produced operation type is not registered in OperationCatalog', function (): void {
|
|
$knownTypes = array_keys(OperationCatalog::labels());
|
|
|
|
$files = File::allFiles(app_path());
|
|
|
|
$discoveredTypes = [];
|
|
|
|
foreach ($files as $file) {
|
|
$path = $file->getRealPath();
|
|
if (! is_string($path)) {
|
|
continue;
|
|
}
|
|
|
|
// Skip the catalog itself to avoid tautology.
|
|
if (str_ends_with($path, '/Support/OperationCatalog.php')) {
|
|
continue;
|
|
}
|
|
|
|
$contents = File::get($path);
|
|
|
|
// Capture common patterns where operation type strings are produced in code.
|
|
// Example: ensureRun(type: 'inventory_sync', ...)
|
|
if (preg_match_all("/(?:\btype\s*:\s*|\btype\b\s*=>\s*)'([a-z0-9_]+(?:\.[a-z0-9_]+)+)'/i", $contents, $matches)) {
|
|
foreach ($matches[1] as $type) {
|
|
$discoveredTypes[] = $type;
|
|
}
|
|
}
|
|
|
|
// Example: if ($run->type === 'inventory_sync')
|
|
if (preg_match_all("/\btype\s*(?:===|!==|==|!=)\s*'([a-z0-9_]+(?:\.[a-z0-9_]+)+)'/i", $contents, $matches)) {
|
|
foreach ($matches[1] as $type) {
|
|
$discoveredTypes[] = $type;
|
|
}
|
|
}
|
|
|
|
// Example: in_array($run->type, ['a.b', 'c.d'], true)
|
|
if (preg_match_all("/\bin_array\([^\)]*\[([^\]]+)\]/i", $contents, $matches)) {
|
|
foreach ($matches[1] as $list) {
|
|
if (preg_match_all("/'([a-z0-9_]+(?:\.[a-z0-9_]+)+)'/i", $list, $inner)) {
|
|
foreach ($inner[1] as $type) {
|
|
$discoveredTypes[] = $type;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$discoveredTypes = array_values(array_unique($discoveredTypes));
|
|
|
|
$unknownTypes = array_values(array_diff($discoveredTypes, $knownTypes));
|
|
|
|
expect($unknownTypes)
|
|
->toBeEmpty();
|
|
})->group('ops-ux');
|