value => 8, Workload::Entra->value => 6, Workload::Exchange->value => 6, Workload::Teams->value => 6, Workload::SecurityCompliance->value => 6, ]; $counts = TenantConfigurationResourceType::query() ->selectRaw('workload, count(*) as aggregate') ->groupBy('workload') ->pluck('aggregate', 'workload') ->map(fn (int|string $count): int => (int) $count); foreach ($expectedByWorkload as $workload => $expectedCount) { expect($counts[$workload] ?? null)->toBe($expectedCount); } expect(TenantConfigurationResourceType::query()->count())->toBe(32) ->and(TenantConfigurationResourceType::query()->whereIn('workload', [ Workload::Defender->value, Workload::Purview->value, Workload::Tenantpilot->value, Workload::Unknown->value, ])->exists())->toBeFalse() ->and(TenantConfigurationResource::query()->count())->toBe(0) ->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0); $m365Rows = TenantConfigurationResourceType::query() ->where('workload', '!=', Workload::Intune->value) ->orderBy('canonical_type') ->get(); foreach ($m365Rows as $row) { expect($row->support_state)->toBe(SupportState::OutOfScope) ->and($row->default_coverage_level)->toBe(CoverageLevel::Detected) ->and($row->default_evidence_state)->toBe(EvidenceState::NotCaptured) ->and($row->default_identity_state)->toBe(IdentityState::Derived) ->and($row->default_claim_state)->toBe(ClaimState::InternalOnly) ->and($row->allows_beta_claims)->toBeFalse() ->and($row->allows_graph_fallback_claims)->toBeFalse() ->and($row->allows_certified_claims)->toBeFalse() ->and($row->metadata['registry_only'])->toBeTrue() ->and($row->metadata['is_full_catalog'])->toBeFalse() ->and($row->metadata['customer_claims_allowed'])->toBeFalse(); expect([ RestoreTier::NotRestorable, RestoreTier::PreviewOnly, ])->toContain($row->restore_tier); } }); it('Spec419 persists inactive M365 planning scopes and leaves active Intune scopes unchanged', function (): void { $requiredScopeKeys = [ 'm365_tcm_registry_detected', 'entra_tcm_registry_detected', 'exchange_tcm_registry_detected', 'teams_tcm_registry_detected', 'security_compliance_tcm_registry_detected', 'm365_tcm_generic_future', 'm365_tcm_certified_none', ]; $scopes = TenantConfigurationSupportedScope::query() ->whereIn('scope_key', $requiredScopeKeys) ->get() ->keyBy('scope_key'); expect($scopes->keys()->sort()->values()->all())->toBe(collect($requiredScopeKeys)->sort()->values()->all()); foreach ($scopes as $scope) { expect($scope->minimum_coverage_level)->toBe(CoverageLevel::Detected) ->and($scope->customer_claims_allowed)->toBeFalse() ->and($scope->is_active)->toBeFalse() ->and($scope->metadata['registry_only'])->toBeTrue() ->and($scope->metadata['is_full_catalog'])->toBeFalse(); } expect(TenantConfigurationSupportedScope::query()->active()->orderBy('scope_key')->pluck('scope_key')->all()) ->toBe([ 'intune_tcm_core', 'intune_tcm_core_with_graph_fallback', ]) ->and(TenantConfigurationSupportedScope::query() ->whereIn('scope_key', [ 'm365_full_coverage', 'm365_certified', 'all_microsoft_365_supported', 'full_tenant_coverage', 'full_m365_restore_ready', ]) ->exists())->toBeFalse(); $aggregate = $scopes['m365_tcm_registry_detected']; expect($aggregate->included_resource_types)->toHaveCount(24) ->and($aggregate->included_resource_types)->toContain('conditionalAccessPolicy') ->toContain('transportRule') ->toContain('meetingPolicy') ->toContain('dlpCompliancePolicy'); }); it('Spec419 keeps registry sync idempotent and non-capturing', function (): void { (new ResourceTypeRegistry)->syncDefaults(); (new ResourceTypeRegistry)->syncDefaults(); (new SupportedScopeResolver)->syncDefaults(); (new SupportedScopeResolver)->syncDefaults(); expect(TenantConfigurationResourceType::query()->count())->toBe(32) ->and(TenantConfigurationSupportedScope::query()->count())->toBe(9) ->and(TenantConfigurationResource::query()->count())->toBe(0) ->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0); }); it('Spec419 rollback deletes only exact Spec419 rows and preserves later non-Intune promotions', function (): void { $futureResourceType = TenantConfigurationResourceType::query() ->where('canonical_type', 'conditionalAccessPolicy') ->where('source_class', 'tcm') ->firstOrFail(); $futureResourceType->update([ 'metadata' => [ ...$futureResourceType->metadata, 'catalog_import_batch' => 'future_entra_promotion', 'registry_only' => false, 'future_spec' => 'preserve_non_intune_after_spec_419', ], ]); $futureScope = TenantConfigurationSupportedScope::query() ->where('scope_key', 'm365_tcm_registry_detected') ->firstOrFail(); $futureScope->update([ 'metadata' => [ ...$futureScope->metadata, 'catalog_import_batch' => 'future_m365_scope_promotion', 'future_spec' => 'preserve_non_intune_after_spec_419', ], ]); $migration = require database_path('migrations/2026_06_26_000419_expand_tenant_configuration_workloads.php'); $migration->down(); expect(TenantConfigurationResourceType::query() ->where('canonical_type', 'conditionalAccessPolicy') ->where('source_class', 'tcm') ->exists())->toBeTrue() ->and(TenantConfigurationResourceType::query() ->where('canonical_type', 'securityDefaults') ->where('source_class', 'tcm') ->exists())->toBeFalse() ->and(TenantConfigurationSupportedScope::query() ->where('scope_key', 'm365_tcm_registry_detected') ->exists())->toBeTrue() ->and(TenantConfigurationSupportedScope::query() ->where('scope_key', 'entra_tcm_registry_detected') ->exists())->toBeFalse(); if (DB::getDriverName() === 'pgsql') { $definition = DB::scalar(<<<'SQL' SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'tenant_configuration_resource_types'::regclass AND conname = 'tenant_config_resource_types_workload_check' SQL); expect((string) $definition)->toContain('intune') ->toContain('entra'); } }); it('Spec419 does not add tenant ownership columns to registry definition tables', function (): void { foreach (['tenant_configuration_resource_types', 'tenant_configuration_supported_scopes'] as $table) { expect(Schema::getColumnListing($table)) ->not->toContain('tenant_id') ->not->toContain('provider_tenant_id') ->not->toContain('entra_tenant_id') ->not->toContain('workspace_id') ->not->toContain('managed_environment_id') ->not->toContain('provider_connection_id'); } }); it('Spec419 stays out of capture clients, v1 adapters, and workload mini-platforms', function (): void { $files = [ app_path('Services/TenantConfiguration/ResourceTypeRegistry.php'), app_path('Services/TenantConfiguration/SupportedScopeResolver.php'), app_path('Services/TenantConfiguration/ClaimGuard.php'), app_path('Support/TenantConfiguration/Workload.php'), database_path('migrations/2026_06_26_000419_expand_tenant_configuration_workloads.php'), ]; $content = collect($files) ->map(fn (string $file): string => file_get_contents($file) ?: '') ->implode("\n"); expect($content) ->not->toContain('GraphClientInterface') ->not->toContain('ProviderGateway') ->not->toContain('StartTenantConfigurationCapture') ->not->toContain('CaptureTenantConfigurationEvidenceJob') ->not->toContain('TenantConfigurationResource::query()->create') ->not->toContain('TenantConfigurationResourceEvidence::query()->create') ->not->toContain('Http::') ->not->toContain('tenant_id') ->not->toContain('ProviderV1') ->not->toContain('LegacyAdapter') ->not->toContain('dual_write') ->not->toContain('namespace App\\Services\\TenantConfiguration\\Entra') ->not->toContain('namespace App\\Services\\TenantConfiguration\\Exchange') ->not->toContain('namespace App\\Services\\TenantConfiguration\\Teams') ->not->toContain('namespace App\\Services\\TenantConfiguration\\Defender') ->not->toContain('namespace App\\Services\\TenantConfiguration\\Purview') ->not->toContain('namespace App\\Services\\TenantConfiguration\\SecurityCompliance') ->not->toContain('create_entra_') ->not->toContain('create_exchange_') ->not->toContain('create_teams_') ->not->toContain('create_defender_') ->not->toContain('create_purview_') ->not->toContain('create_security_compliance_'); }); it('Spec419 updates the PostgreSQL workload check constraint', function (): void { if (DB::getDriverName() !== 'pgsql') { $this->markTestSkipped('PostgreSQL-specific workload check proof runs in the pgsql lane.'); } $definition = DB::scalar(<<<'SQL' SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'tenant_configuration_resource_types'::regclass AND conname = 'tenant_config_resource_types_workload_check' SQL); foreach (Workload::values() as $workload) { expect((string) $definition)->toContain($workload); } });