count(); foreach (spec430IncludedTypes() as $canonicalType) { app(CoverageSourceContractResolver::class)->resolve(spec430NoPromotionResourceType($canonicalType)); } expect(OperationRun::query()->count())->toBe($beforeRuns) ->and(TenantConfigurationResource::query()->count())->toBe(0) ->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0); }); it('Spec430 capture path skips included adapter contracts without provider calls or evidence rows', function (): void { app(ResourceTypeRegistry::class)->syncDefaults(); [$user, $environment] = createMinimalUserWithTenant(role: 'owner'); $connection = ProviderConnection::factory()->withCredential()->create([ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'scopes_granted' => ['Exchange.ManageAsApp'], ]); $graph = spec430NoPromotionGraphClient(); app()->instance(GraphClientInterface::class, $graph); $run = OperationRun::factory()->withUser($user)->forTenant($environment)->create([ 'type' => OperationRunType::TenantConfigurationCapture->value, 'status' => OperationRunStatus::Queued->value, 'outcome' => OperationRunOutcome::Pending->value, 'context' => [ 'target_scope' => [ 'workspace_id' => (int) $environment->workspace_id, 'managed_environment_id' => (int) $environment->getKey(), 'provider_connection_id' => (int) $connection->getKey(), ], 'resource_types' => spec430IncludedTypes(), 'required_capability' => 'evidence.manage', ], ]); $result = app(GenericContentEvidenceCaptureService::class)->capture( tenant: $environment, providerConnection: $connection, operationRun: $run, canonicalTypes: spec430IncludedTypes(), ); expect($graph->calls)->toBe([]) ->and($result['summary_counts'])->toMatchArray([ 'total' => 3, 'processed' => 3, 'succeeded' => 0, 'skipped' => 3, 'failed' => 0, 'errors_recorded' => 0, ]) ->and($result['run_outcome'])->toBe(OperationRunOutcome::Blocked->value) ->and(TenantConfigurationResource::query()->count())->toBe(0) ->and(TenantConfigurationResourceEvidence::query()->count())->toBe(0); foreach (collect($result['outcomes'])->keyBy('canonical_type') as $canonicalType => $outcome) { expect(spec430IncludedTypes())->toContain($canonicalType) ->and($outcome['outcome'])->toBe(CaptureOutcome::BlockedMissingContract->value) ->and($outcome['reason_code'])->toBe('contract_verified_pending_capture') ->and($outcome['source_contract_key'])->toBe('exchange_powershell.'.$canonicalType); } }); it('Spec430 registry defaults remain internal non-evidence non-certified and non-restorable', function (): void { app(ResourceTypeRegistry::class)->syncDefaults(); $rows = TenantConfigurationResourceType::query() ->whereIn('canonical_type', spec430IncludedTypes()) ->orderBy('canonical_type') ->get(); expect($rows)->toHaveCount(3); foreach ($rows as $row) { expect($row->default_coverage_level)->toBe(CoverageLevel::Detected) ->and($row->default_evidence_state)->toBe(EvidenceState::NotCaptured) ->and($row->default_claim_state)->toBe(ClaimState::InternalOnly) ->and($row->allows_certified_claims)->toBeFalse() ->and($row->allows_graph_fallback_claims)->toBeFalse() ->and($row->metadata['customer_claims_allowed'])->toBeFalse() ->and($row->restore_tier)->not->toBe(RestoreTier::Restorable); } }); it('Spec430 introduces no UI routes navigation legacy ownership or Exchange mini-platform artifacts', function (): void { $runtimeFiles = [ app_path('Services/TenantConfiguration/ExchangePowerShellCommandContracts.php'), app_path('Services/TenantConfiguration/CoverageSourceContractResolver.php'), app_path('Services/TenantConfiguration/ResourceTypeRegistry.php'), app_path('Services/TenantConfiguration/SupportedScopeResolver.php'), app_path('Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php'), ]; $runtimeSource = collect($runtimeFiles) ->map(fn (string $file): string => file_get_contents($file) ?: '') ->implode("\n"); expect($runtimeSource) ->not->toContain('tenant_id') ->not->toContain('provider_tenant_id') ->not->toContain('entra_tenant_id') ->and(File::exists(app_path('Services/TenantConfiguration/Exchange')))->toBeFalse() ->and(File::exists(app_path('Services/Providers/ExchangeProviderAdapter.php')))->toBeFalse() ->and(glob(base_path('routes/*exchange*')) ?: [])->toBe([]) ->and(glob(resource_path('views/**/*exchange*')) ?: [])->toBe([]) ->and(glob(app_path('Filament/**/*ExchangePowerShell*')) ?: [])->toBe([]); }); /** * @return list */ function spec430IncludedTypes(): array { return ['transportRule', 'remoteDomain', 'inboundConnector']; } function spec430NoPromotionResourceType(string $canonicalType): TenantConfigurationResourceType { $definition = collect(ResourceTypeRegistry::defaultDefinitions()) ->firstWhere('canonical_type', $canonicalType); expect($definition)->not->toBeNull("Missing default resource type definition for {$canonicalType}."); return new TenantConfigurationResourceType($definition); } function spec430NoPromotionGraphClient(): GraphClientInterface { return new class implements GraphClientInterface { /** * @var list}> */ public array $calls = []; public function listPolicies(string $policyType, array $options = []): GraphResponse { $this->calls[] = ['policy_type' => $policyType, 'options' => $options]; return new GraphResponse(true, [['id' => 'unexpected-provider-call']]); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(false, [], 501); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(false, [], 501); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(false, [], 501); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(false, [], 501); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(false, [], 501); } }; }