Problem: Beim Hinzufügen zu einem Backup Set kann ein lokaler “Reuse” dazu führen, dass ein Backup nicht den aktuellen Intune-Stand reflektiert, wenn last_synced_at nicht frisch ist. Lösung: BackupService führt beim Add immer orchestrated capture aus (Graph Fetch), damit “Backup = current state” gilt. Trotzdem kein unnötiges Version-Wachstum: PolicyCaptureOrchestrator re-used bestehende PolicyVersions via Snapshot-Hash, wenn sich nichts geändert hat. Tests: Added BackupServiceVersionReuseTest.php Specs: Updated spec.md + plan.md + tasks checked off. Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #22
145 lines
4.6 KiB
PHP
145 lines
4.6 KiB
PHP
<?php
|
|
|
|
use App\Models\BackupSet;
|
|
use App\Models\Policy;
|
|
use App\Models\PolicyVersion;
|
|
use App\Models\Tenant;
|
|
use App\Models\User;
|
|
use App\Services\Intune\BackupService;
|
|
use App\Services\Intune\PolicyCaptureOrchestrator;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Mockery\MockInterface;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
it('reuses latest policy version for backup when it is up-to-date and satisfies capture options', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$tenant->makeCurrent();
|
|
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$backupSet = BackupSet::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'status' => 'completed',
|
|
]);
|
|
|
|
$policy = Policy::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'last_synced_at' => now(),
|
|
'ignored_at' => null,
|
|
]);
|
|
|
|
$existingVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'captured_at' => now(),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
|
|
'assignments' => null,
|
|
'scope_tags' => null,
|
|
]);
|
|
|
|
$this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($existingVersion) {
|
|
$mock->shouldReceive('capture')
|
|
->once()
|
|
->andReturn([
|
|
'version' => $existingVersion,
|
|
'captured' => [
|
|
'payload' => $existingVersion->snapshot,
|
|
'assignments' => $existingVersion->assignments,
|
|
'scope_tags' => $existingVersion->scope_tags,
|
|
'metadata' => [],
|
|
],
|
|
]);
|
|
});
|
|
|
|
$service = app(BackupService::class);
|
|
|
|
$service->addPoliciesToSet(
|
|
tenant: $tenant,
|
|
backupSet: $backupSet,
|
|
policyIds: [$policy->id],
|
|
actorEmail: $user->email,
|
|
actorName: $user->name,
|
|
includeAssignments: false,
|
|
includeScopeTags: false,
|
|
includeFoundations: false,
|
|
);
|
|
|
|
expect(PolicyVersion::query()->where('policy_id', $policy->id)->count())->toBe(1);
|
|
|
|
$item = $backupSet->items()->first();
|
|
expect($item)->not->toBeNull();
|
|
expect($item->policy_version_id)->toBe($existingVersion->id);
|
|
});
|
|
|
|
it('captures a new policy version for backup when no suitable existing version is available', function () {
|
|
$tenant = Tenant::factory()->create();
|
|
$tenant->makeCurrent();
|
|
|
|
$user = User::factory()->create();
|
|
$this->actingAs($user);
|
|
|
|
$backupSet = BackupSet::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'status' => 'completed',
|
|
]);
|
|
|
|
$policy = Policy::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'last_synced_at' => now(),
|
|
'ignored_at' => null,
|
|
]);
|
|
|
|
$staleVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'version_number' => 1,
|
|
'captured_at' => now()->subDays(2),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name],
|
|
]);
|
|
|
|
$policy->update(['last_synced_at' => now()]);
|
|
|
|
$this->mock(PolicyCaptureOrchestrator::class, function (MockInterface $mock) use ($policy, $tenant) {
|
|
$mock->shouldReceive('capture')
|
|
->once()
|
|
->andReturnUsing(function () use ($policy, $tenant) {
|
|
$newVersion = PolicyVersion::factory()->create([
|
|
'tenant_id' => $tenant->id,
|
|
'policy_id' => $policy->id,
|
|
'version_number' => 2,
|
|
'captured_at' => now(),
|
|
'snapshot' => ['id' => $policy->external_id, 'name' => $policy->display_name, 'changed' => true],
|
|
]);
|
|
|
|
return [
|
|
'version' => $newVersion,
|
|
'captured' => [
|
|
'payload' => $newVersion->snapshot,
|
|
'assignments' => null,
|
|
'scope_tags' => null,
|
|
'metadata' => [],
|
|
],
|
|
];
|
|
});
|
|
});
|
|
|
|
$service = app(BackupService::class);
|
|
|
|
$service->addPoliciesToSet(
|
|
tenant: $tenant,
|
|
backupSet: $backupSet,
|
|
policyIds: [$policy->id],
|
|
actorEmail: $user->email,
|
|
actorName: $user->name,
|
|
includeAssignments: false,
|
|
includeScopeTags: false,
|
|
includeFoundations: false,
|
|
);
|
|
|
|
$item = $backupSet->items()->first();
|
|
expect($item)->not->toBeNull();
|
|
expect($item->policy_version_id)->not->toBe($staleVersion->id);
|
|
});
|