TenantAtlas/tests/Feature/BackupServiceVersionReuseTest.php
Ahmed Darrazi 1145e45fb9 feat: always capture policy when adding to backup
Admin-first: always run orchestrated capture when adding policies to a backup set so backups reflect current Intune state. Still avoids redundant PolicyVersion growth via orchestrator snapshot-hash reuse. Adds feature tests and updates spec/plan/tasks.
2026-01-02 15:32:00 +01:00

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);
});