bind(GraphClientInterface::class, fn () => new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true, ['payload' => []]); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true, []); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true, []); } }); $tenant = Tenant::create([ 'tenant_id' => 'tenant-1', 'name' => 'Tenant One', 'metadata' => [], ]); $policy = Policy::create([ 'tenant_id' => $tenant->id, 'external_id' => 'policy-1', 'policy_type' => 'deviceConfiguration', 'display_name' => 'Policy A', 'platform' => 'windows', ]); $backupSet = BackupSet::create([ 'tenant_id' => $tenant->id, 'name' => 'Backup', 'status' => 'completed', 'item_count' => 1, ]); $backupItem = BackupItem::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'policy_id' => $policy->id, 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => ['foo' => 'bar'], ]); $user = User::factory()->create(['email' => 'tester@example.com']); $this->actingAs($user); $service = app(RestoreService::class); $run = $service->execute( tenant: $tenant, backupSet: $backupSet, selectedItemIds: [$backupItem->id], dryRun: false, actorEmail: $user->email, actorName: $user->name, ); expect($run->status)->toBe('completed'); expect($run->results[0]['status'])->toBe('applied'); $this->assertDatabaseHas('audit_logs', [ 'action' => 'restore.executed', 'resource_id' => (string) $run->id, ]); expect(PolicyVersion::where('policy_id', $policy->id)->count())->toBe(1); }); test('restore execution records foundation mappings', function () { config()->set('tenantpilot.foundation_types', [ [ 'type' => 'assignmentFilter', 'label' => 'Assignment Filter', 'category' => 'Foundations', 'platform' => 'all', 'endpoint' => 'deviceManagement/assignmentFilters', 'backup' => 'full', 'restore' => 'enabled', 'risk' => 'low', ], ]); $tenant = Tenant::factory()->create(); $backupSet = BackupSet::factory()->for($tenant)->create(); $backupItem = BackupItem::factory() ->for($tenant) ->for($backupSet) ->state([ 'policy_id' => null, 'policy_identifier' => 'filter-1', 'policy_type' => 'assignmentFilter', 'platform' => 'all', 'payload' => [ 'id' => 'filter-1', 'displayName' => 'Filter One', ], 'metadata' => [ 'displayName' => 'Filter One', ], ]) ->create(); $entries = [ [ 'type' => 'assignmentFilter', 'sourceId' => 'filter-1', 'sourceName' => 'Filter One', 'decision' => 'created', 'targetId' => 'filter-2', 'targetName' => 'Filter One', ], ]; $this->mock(FoundationMappingService::class, function (MockInterface $mock) use ($entries) { $mock->shouldReceive('map') ->twice() ->andReturn([ 'entries' => $entries, 'mapping' => ['filter-1' => 'filter-2'], 'failed' => 0, 'skipped' => 0, ]); }); $user = User::factory()->create(['email' => 'tester@example.com']); $this->actingAs($user); $service = app(RestoreService::class); $run = $service->execute( tenant: $tenant, backupSet: $backupSet, selectedItemIds: [$backupItem->id], dryRun: false, actorEmail: $user->email, actorName: $user->name, ); expect($run->status)->toBe('completed'); expect($run->results)->toHaveCount(1); expect($run->results[0]['decision'])->toBe('created'); $this->assertDatabaseHas('audit_logs', [ 'action' => 'restore.foundation.created', 'resource_id' => (string) $run->id, ]); }); test('restore execution records compliance notification mapping outcomes', function () { app()->bind(GraphClientInterface::class, fn () => new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true, ['payload' => []]); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true, []); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true, []); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true, []); } }); $tenant = Tenant::create([ 'tenant_id' => 'tenant-3', 'name' => 'Tenant Three', 'metadata' => [], ]); $policy = Policy::create([ 'tenant_id' => $tenant->id, 'external_id' => 'policy-3', 'policy_type' => 'deviceCompliancePolicy', 'display_name' => 'Compliance Policy', 'platform' => 'windows', ]); $backupSet = BackupSet::create([ 'tenant_id' => $tenant->id, 'name' => 'Backup', 'status' => 'completed', 'item_count' => 1, ]); $backupItem = BackupItem::create([ 'tenant_id' => $tenant->id, 'backup_set_id' => $backupSet->id, 'policy_id' => $policy->id, 'policy_identifier' => $policy->external_id, 'policy_type' => $policy->policy_type, 'platform' => $policy->platform, 'payload' => [ 'scheduledActionsForRule' => [ [ 'ruleName' => 'Password', 'scheduledActionConfigurations' => [ [ 'actionType' => 'notification', 'notificationTemplateId' => 'template-1', ], ], ], ], ], ]); $user = User::factory()->create(['email' => 'tester@example.com']); $this->actingAs($user); $service = app(RestoreService::class); $run = $service->execute( tenant: $tenant, backupSet: $backupSet, selectedItemIds: [$backupItem->id], dryRun: false, actorEmail: $user->email, actorName: $user->name, ); expect($run->status)->toBe('partial'); expect($run->results[0]['status'])->toBe('partial'); expect($run->results[0]['compliance_action_summary']['skipped'] ?? null)->toBe(1); $this->assertDatabaseHas('audit_logs', [ 'action' => 'restore.compliance.actions.mapped', 'resource_id' => (string) $run->id, ]); });