## Summary <!-- Kurz: Was ändert sich und warum? --> ## Spec-Driven Development (SDD) - [ ] Es gibt eine Spec unter `specs/<NNN>-<feature>/` - [ ] Enthaltene Dateien: `plan.md`, `tasks.md`, `spec.md` - [ ] Spec beschreibt Verhalten/Acceptance Criteria (nicht nur Implementation) - [ ] Wenn sich Anforderungen während der Umsetzung geändert haben: Spec/Plan/Tasks wurden aktualisiert ## Implementation - [ ] Implementierung entspricht der Spec - [ ] Edge cases / Fehlerfälle berücksichtigt - [ ] Keine unbeabsichtigten Änderungen außerhalb des Scopes ## Tests - [ ] Tests ergänzt/aktualisiert (Pest/PHPUnit) - [ ] Relevante Tests lokal ausgeführt (`./vendor/bin/sail artisan test` oder `php artisan test`) ## Migration / Config / Ops (falls relevant) - [ ] Migration(en) enthalten und getestet - [ ] Rollback bedacht (rückwärts kompatibel, sichere Migration) - [ ] Neue Env Vars dokumentiert (`.env.example` / Doku) - [ ] Queue/cron/storage Auswirkungen geprüft ## UI (Filament/Livewire) (falls relevant) - [ ] UI-Flows geprüft - [ ] Screenshots/Notizen hinzugefügt ## Notes <!-- Links, Screenshots, Follow-ups, offene Punkte --> Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #3
176 lines
5.9 KiB
PHP
176 lines
5.9 KiB
PHP
<?php
|
|
|
|
use App\Models\SettingsCatalogDefinition;
|
|
use App\Services\Graph\GraphClientInterface;
|
|
use App\Services\Graph\GraphResponse;
|
|
use App\Services\Intune\SettingsCatalogDefinitionResolver;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
// Clear cache before each test
|
|
Cache::flush();
|
|
});
|
|
|
|
it('uses cached definitions from database on second call', function () {
|
|
// Arrange
|
|
$definitionId = 'device_vendor_msft_policy_config_defender_allowbehaviormonitoring';
|
|
|
|
// Pre-populate cache
|
|
SettingsCatalogDefinition::create([
|
|
'definition_id' => $definitionId,
|
|
'display_name' => 'Allow Behavior Monitoring',
|
|
'description' => 'Enable behavior monitoring',
|
|
'help_text' => 'This setting controls...',
|
|
'raw' => ['id' => $definitionId],
|
|
]);
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
// Should NOT call Graph API
|
|
$mockClient->shouldNotReceive('request');
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act
|
|
$result = $resolver->resolve([$definitionId]);
|
|
|
|
// Assert
|
|
expect($result)->toHaveCount(1);
|
|
expect($result[$definitionId])->toMatchArray([
|
|
'displayName' => 'Allow Behavior Monitoring',
|
|
'description' => 'Enable behavior monitoring',
|
|
]);
|
|
});
|
|
|
|
it('returns fallback for missing definitions with prettified ID', function () {
|
|
// Arrange
|
|
$definitionId = 'device_vendor_msft_policy_config_unknown_setting';
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
$mockResponse = Mockery::mock(GraphResponse::class);
|
|
$mockResponse->shouldReceive('successful')->andReturn(false);
|
|
|
|
$mockClient->shouldReceive('request')
|
|
->once()
|
|
->andReturn($mockResponse);
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act
|
|
$result = $resolver->resolve([$definitionId]);
|
|
|
|
// Assert
|
|
expect($result)->toHaveCount(1);
|
|
expect($result[$definitionId])->toMatchArray([
|
|
'displayName' => 'Device Vendor Msft Policy Config Unknown Setting',
|
|
'description' => null,
|
|
'isFallback' => true,
|
|
]);
|
|
});
|
|
|
|
it('resolveOne method returns single definition from cache', function () {
|
|
// Arrange
|
|
$definitionId = 'device_vendor_msft_policy_config_connectivity_disallownetworkconnectivityactivetest';
|
|
|
|
SettingsCatalogDefinition::create([
|
|
'definition_id' => $definitionId,
|
|
'display_name' => 'Disallow Network Connectivity Active Test',
|
|
'description' => 'Disable NCSI probes',
|
|
'raw' => ['id' => $definitionId],
|
|
]);
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
$mockClient->shouldNotReceive('request');
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act
|
|
$result = $resolver->resolveOne($definitionId);
|
|
|
|
// Assert
|
|
expect($result)->toMatchArray([
|
|
'displayName' => 'Disallow Network Connectivity Active Test',
|
|
'description' => 'Disable NCSI probes',
|
|
]);
|
|
});
|
|
|
|
it('handles batch of definitions with mixed cached and uncached', function () {
|
|
// Arrange
|
|
$cachedId = 'device_vendor_msft_policy_config_cached_setting';
|
|
$uncachedId = 'device_vendor_msft_policy_config_uncached_setting';
|
|
|
|
// Pre-cache one definition
|
|
SettingsCatalogDefinition::create([
|
|
'definition_id' => $cachedId,
|
|
'display_name' => 'Cached Setting',
|
|
'description' => 'This was cached',
|
|
'raw' => ['id' => $cachedId],
|
|
]);
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
$mockResponse = Mockery::mock(GraphResponse::class);
|
|
$mockResponse->shouldReceive('successful')->andReturn(false);
|
|
|
|
$mockClient->shouldReceive('request')
|
|
->once()
|
|
->with('GET', "/deviceManagement/configurationSettings/{$uncachedId}")
|
|
->andReturn($mockResponse);
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act
|
|
$result = $resolver->resolve([$cachedId, $uncachedId]);
|
|
|
|
// Assert
|
|
expect($result)->toHaveCount(2);
|
|
expect($result[$cachedId]['displayName'])->toBe('Cached Setting');
|
|
expect($result[$uncachedId]['displayName'])->toBe('Device Vendor Msft Policy Config Uncached Setting'); // Fallback
|
|
expect($result[$uncachedId]['isFallback'])->toBeTrue();
|
|
});
|
|
|
|
it('warmCache method pre-populates cache without throwing', function () {
|
|
// Arrange
|
|
$definitionId = 'device_vendor_msft_policy_config_firewall_enablefirewall';
|
|
|
|
SettingsCatalogDefinition::create([
|
|
'definition_id' => $definitionId,
|
|
'display_name' => 'Enable Firewall',
|
|
'description' => 'Turn Windows Firewall on or off',
|
|
'raw' => ['id' => $definitionId],
|
|
]);
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
$mockClient->shouldNotReceive('request');
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act & Assert (should not throw)
|
|
expect(fn () => $resolver->warmCache([$definitionId]))->not->toThrow(Exception::class);
|
|
|
|
// Cache should be populated
|
|
$cached = SettingsCatalogDefinition::where('definition_id', $definitionId)->first();
|
|
expect($cached)->not->toBeNull();
|
|
expect($cached->display_name)->toBe('Enable Firewall');
|
|
});
|
|
|
|
it('warmCache handles errors gracefully without throwing', function () {
|
|
// Arrange
|
|
$definitionIds = ['device_vendor_msft_policy_config_test'];
|
|
|
|
$mockClient = Mockery::mock(GraphClientInterface::class);
|
|
$mockClient->shouldReceive('request')
|
|
->once()
|
|
->andThrow(new Exception('Graph API error'));
|
|
|
|
$resolver = new SettingsCatalogDefinitionResolver($mockClient);
|
|
|
|
// Act & Assert (should not throw)
|
|
expect(fn () => $resolver->warmCache($definitionIds))->not->toThrow(Exception::class);
|
|
|
|
// Cache should remain empty
|
|
$cached = SettingsCatalogDefinition::where('definition_id', $definitionIds[0])->first();
|
|
expect($cached)->toBeNull();
|
|
});
|