create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCompare->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => [ 'baseline_compare' => [ 'evidence_gaps' => [ 'count' => 1, 'by_reason' => ['policy_not_found' => 1], 'subjects' => [ 'policy_not_found' => [ 'deviceConfiguration|legacy-policy-gap', ], ], ], ], ], ]); $legacyCapture = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCapture->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => [ 'baseline_capture' => [ 'gaps' => [ 'count' => 1, 'by_reason' => ['policy_not_found' => 1], 'subjects' => [ 'policy_not_found' => [ 'deviceConfiguration|legacy-capture-gap', ], ], ], ], ], ]); $structuredCompare = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCompare->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => BaselineSubjectResolutionFixtures::compareContext([ BaselineSubjectResolutionFixtures::structuredGap(), ]), ]); expect($legacyCompare->hasLegacyBaselineGapPayload())->toBeTrue() ->and($legacyCapture->hasLegacyBaselineGapPayload())->toBeTrue() ->and($structuredCompare->hasStructuredBaselineGapPayload())->toBeTrue() ->and($structuredCompare->hasLegacyBaselineGapPayload())->toBeFalse(); $this->artisan('tenantpilot:baselines:purge-legacy-gap-runs') ->expectsOutputToContain('Dry run: matched 2 legacy baseline run(s). Re-run with --force to delete them.') ->assertSuccessful(); expect(OperationRun::query()->whereKey($legacyCompare->getKey())->exists())->toBeTrue() ->and(OperationRun::query()->whereKey($legacyCapture->getKey())->exists())->toBeTrue() ->and(OperationRun::query()->whereKey($structuredCompare->getKey())->exists())->toBeTrue(); }); it('deletes only legacy baseline gap runs when forced', function (): void { [, $tenant] = createUserWithTenant(role: 'owner'); $legacyCompare = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCompare->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => [ 'baseline_compare' => [ 'evidence_gaps' => [ 'count' => 1, 'by_reason' => ['policy_not_found' => 1], 'subjects' => [ 'policy_not_found' => [ 'deviceConfiguration|legacy-policy-gap', ], ], ], ], ], ]); $structuredCompare = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCompare->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => BaselineSubjectResolutionFixtures::compareContext([ BaselineSubjectResolutionFixtures::structuredGap(), ]), ]); $structuredCapture = OperationRun::factory()->create([ 'tenant_id' => (int) $tenant->getKey(), 'workspace_id' => (int) $tenant->workspace_id, 'type' => OperationRunType::BaselineCapture->value, 'status' => OperationRunStatus::Completed->value, 'outcome' => OperationRunOutcome::PartiallySucceeded->value, 'context' => BaselineSubjectResolutionFixtures::captureContext([ BaselineSubjectResolutionFixtures::structuredGap(), ]), ]); $this->artisan('tenantpilot:baselines:purge-legacy-gap-runs', ['--force' => true]) ->expectsOutputToContain('Deleted 1 legacy baseline run(s).') ->assertSuccessful(); expect(OperationRun::query()->whereKey($legacyCompare->getKey())->exists())->toBeFalse() ->and(OperationRun::query()->whereKey($structuredCompare->getKey())->exists())->toBeTrue() ->and(OperationRun::query()->whereKey($structuredCapture->getKey())->exists())->toBeTrue(); });