107 lines
3.5 KiB
PHP
107 lines
3.5 KiB
PHP
<?php
|
|
|
|
use App\Domain\Inventory\Enums\SyncRunStatus;
|
|
use App\Domain\Inventory\Models\SnipeAsset;
|
|
use App\Domain\Inventory\Models\SyncRun;
|
|
use App\Domain\Inventory\Services\AssetSyncService;
|
|
use App\Integrations\SnipeIt\Exceptions\SnipeItApiException;
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
beforeEach(function () {
|
|
config()->set('services.snipe_it', [
|
|
'url' => 'https://inventory.example.test',
|
|
'read_token' => 'read-token-for-tests',
|
|
'timeout' => 2,
|
|
'page_size' => 1,
|
|
'retries' => 1,
|
|
'retry_delay_ms' => 0,
|
|
]);
|
|
});
|
|
|
|
it('sincroniza por páginas e desativa apenas ativos ausentes', function () {
|
|
$stale = SnipeAsset::factory()->create([
|
|
'snipe_asset_id' => 99,
|
|
'asset_tag' => 'ANTIGO-99',
|
|
'active' => true,
|
|
'synced_at' => now()->subDay(),
|
|
]);
|
|
|
|
Http::fakeSequence()
|
|
->push(['total' => 2, 'rows' => [snipeSyncRow(1, 'EQ-001')]])
|
|
->push(['total' => 2, 'rows' => [snipeSyncRow(2, 'EQ-002')]]);
|
|
|
|
$run = app(AssetSyncService::class)->sync();
|
|
|
|
expect($run->status)->toBe(SyncRunStatus::Succeeded)
|
|
->and($run->records_processed)->toBe(2)
|
|
->and($run->records_created)->toBe(2)
|
|
->and($run->records_disabled)->toBe(1)
|
|
->and($stale->refresh()->active)->toBeFalse();
|
|
|
|
$this->assertDatabaseHas('snipe_assets', [
|
|
'snipe_asset_id' => 1,
|
|
'asset_tag' => 'EQ-001',
|
|
'name' => 'Câmara & tripé',
|
|
'category_name' => 'Audiovisual',
|
|
'requestable' => true,
|
|
'active' => true,
|
|
]);
|
|
});
|
|
|
|
it('não desativa ativos quando uma página remota falha', function () {
|
|
$existing = SnipeAsset::factory()->create([
|
|
'snipe_asset_id' => 99,
|
|
'asset_tag' => 'PRESERVAR-99',
|
|
'active' => true,
|
|
'synced_at' => now()->subDay(),
|
|
]);
|
|
|
|
Http::fakeSequence()
|
|
->push(['total' => 2, 'rows' => [snipeSyncRow(1, 'EQ-001')]])
|
|
->push(['message' => 'failure'], 500);
|
|
|
|
expect(fn () => app(AssetSyncService::class)->sync())
|
|
->toThrow(SnipeItApiException::class, 'HTTP 500');
|
|
|
|
expect($existing->refresh()->active)->toBeTrue();
|
|
|
|
$run = SyncRun::query()->latest('id')->firstOrFail();
|
|
expect($run->status)->toBe(SyncRunStatus::Failed)
|
|
->and($run->records_processed)->toBe(1)
|
|
->and($run->records_disabled)->toBe(0);
|
|
});
|
|
|
|
it('não conta como atualização um ativo cujo conteúdo não mudou', function () {
|
|
Http::fake([
|
|
'*' => Http::response(['total' => 1, 'rows' => [snipeSyncRow(1, 'EQ-001')]]),
|
|
]);
|
|
|
|
app(AssetSyncService::class)->sync();
|
|
$secondRun = app(AssetSyncService::class)->sync();
|
|
|
|
expect($secondRun->records_processed)->toBe(1)
|
|
->and($secondRun->records_created)->toBe(0)
|
|
->and($secondRun->records_updated)->toBe(0)
|
|
->and($secondRun->records_disabled)->toBe(0);
|
|
});
|
|
|
|
/** @return array<string, mixed> */
|
|
function snipeSyncRow(int $id, string $tag): array
|
|
{
|
|
return [
|
|
'id' => $id,
|
|
'asset_tag' => $tag,
|
|
'name' => 'Câmara & tripé',
|
|
'serial' => "SERIAL-{$id}",
|
|
'model' => ['id' => 20, 'name' => 'Lumix'],
|
|
'category' => ['id' => 30, 'name' => 'Audiovisual'],
|
|
'manufacturer' => ['id' => 40, 'name' => 'Panasonic'],
|
|
'status_label' => ['id' => 2, 'name' => 'Pronto a utilizar'],
|
|
'rtd_location' => ['id' => 5, 'name' => 'Armazém central'],
|
|
'requestable' => true,
|
|
'assigned_to' => null,
|
|
'image' => 'https://inventory.example.test/uploads/asset.png',
|
|
'updated_at' => ['datetime' => '2026-08-04T12:00:00+00:00'],
|
|
];
|
|
}
|