registry = app(GraphContractRegistry::class); }); it('preserves @odata.type with actual choiceSettingValue data structure', function () { $settings = [ [ 'id' => '0', 'settingInstance' => [ '@odata.type' => '#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance', 'choiceSettingValue' => [ 'value' => 'device_vendor_msft_passportforwork_biometrics_usebiometrics_true', 'children' => [], 'settingValueTemplateReference' => null, ], 'settingDefinitionId' => 'device_vendor_msft_passportforwork_biometrics_usebiometrics', 'auditRuleInformation' => null, 'settingInstanceTemplateReference' => null, ], ], ]; $sanitized = $this->registry->sanitizeSettingsApplyPayload('settingsCatalogPolicy', $settings); expect($sanitized)->toBeArray(); expect(count($sanitized))->toBe(1); // Top-level id should be stripped expect(array_key_exists('id', $sanitized[0]))->toBeFalse(); // settingInstance should exist expect(isset($sanitized[0]['settingInstance']))->toBeTrue(); // @odata.type should be preserved inside settingInstance expect(isset($sanitized[0]['settingInstance']['@odata.type']))->toBeTrue(); expect($sanitized[0]['settingInstance']['@odata.type'])->toBe('#microsoft.graph.deviceManagementConfigurationChoiceSettingInstance'); // choiceSettingValue should still exist expect(isset($sanitized[0]['settingInstance']['choiceSettingValue']))->toBeTrue(); expect($sanitized[0]['settingInstance']['choiceSettingValue']['value'])->toBe('device_vendor_msft_passportforwork_biometrics_usebiometrics_true'); // Null values should be preserved (Graph might need them) expect(array_key_exists('settingValueTemplateReference', $sanitized[0]['settingInstance']['choiceSettingValue']))->toBeTrue(); }); it('exposes autopilot assignments paths', function () { $contract = $this->registry->get('windowsAutopilotDeploymentProfile'); expect($contract)->not->toBeEmpty(); expect($contract['assignments_list_path'] ?? null) ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments'); expect($contract['assignments_create_path'] ?? null) ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments'); expect($contract['assignments_delete_path'] ?? null) ->toBe('/deviceManagement/windowsAutopilotDeploymentProfiles/{id}/assignments/{assignmentId}'); expect($this->registry->matchesTypeFamily( 'windowsAutopilotDeploymentProfile', '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile' ))->toBeTrue(); expect($this->registry->matchesTypeFamily( 'windowsAutopilotDeploymentProfile', '#microsoft.graph.activeDirectoryWindowsAutopilotDeploymentProfile' ))->toBeTrue(); }); it('sanitizes autopilot update payload by stripping odata and assignments', function () { $payload = [ '@odata.type' => '#microsoft.graph.azureADWindowsAutopilotDeploymentProfile', 'id' => 'profile-1', 'displayName' => 'Autopilot Profile', 'assignments' => [['id' => 'assignment-1']], 'managementServiceAppId' => 'service-app', 'outOfBoxExperienceSetting' => ['deviceUsageType' => 'shared'], 'hardwareHashExtractionEnabled' => true, 'locale' => 'de-DE', ]; $sanitized = $this->registry->sanitizeUpdatePayload('windowsAutopilotDeploymentProfile', $payload); expect($sanitized)->toHaveKey('displayName'); expect($sanitized)->toHaveKey('@odata.type'); expect($sanitized)->not->toHaveKey('id'); expect($sanitized)->not->toHaveKey('assignments'); expect($sanitized)->not->toHaveKey('managementServiceAppId'); expect($sanitized)->not->toHaveKey('outOfBoxExperienceSetting'); expect($sanitized)->not->toHaveKey('hardwareHashExtractionEnabled'); expect($sanitized)->not->toHaveKey('locale'); }); it('exposes compliance policy expand for scheduled actions', function () { $contract = $this->registry->get('deviceCompliancePolicy'); expect($contract)->not->toBeEmpty(); expect($contract['allowed_expand'] ?? []) ->toContain('scheduledActionsForRule($expand=scheduledActionConfigurations)'); }); it('exposes mobile app assignment endpoints and type family', function () { $contract = $this->registry->get('mobileApp'); expect($contract)->not->toBeEmpty(); expect($contract['allowed_select'] ?? [])->toContain('roleScopeTagIds'); expect($contract['assignments_list_path'] ?? null) ->toBe('/deviceAppManagement/mobileApps/{id}/assignments'); expect($contract['assignments_create_path'] ?? null) ->toBe('/deviceAppManagement/mobileApps/{id}/assign'); expect($contract['assignments_payload_key'] ?? null) ->toBe('mobileAppAssignments'); expect($this->registry->matchesTypeFamily('mobileApp', '#microsoft.graph.win32LobApp'))->toBeTrue(); expect($this->registry->matchesTypeFamily('mobileApp', '#microsoft.graph.iosVppApp'))->toBeTrue(); }); it('exposes app protection assignment endpoints and type family', function () { $contract = $this->registry->get('appProtectionPolicy'); expect($contract)->not->toBeEmpty(); expect($contract['assignments_list_path'] ?? null) ->toBe('/deviceAppManagement/managedAppPolicies/{id}/assignments'); expect($contract['assignments_create_path'] ?? null) ->toBe('/deviceAppManagement/managedAppPolicies/{id}/assign'); expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.iosManagedAppProtection'))->toBeTrue(); expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.androidManagedAppProtection'))->toBeTrue(); expect($this->registry->matchesTypeFamily('appProtectionPolicy', '#microsoft.graph.targetedManagedAppConfiguration'))->toBeFalse(); }); it('omits role scope tags from assignment filter selects', function () { $contract = $this->registry->get('assignmentFilter'); expect($contract)->not->toBeEmpty(); expect($contract['allowed_select'] ?? []) ->not->toContain('roleScopeTagIds'); });