Files

261 lines
11 KiB
PHP
Raw Permalink Normal View History

<?php
namespace MintyPHP\Tests\Module\Helpdesk\Service;
use MintyPHP\Module\Helpdesk\Repository\HandoverRepository;
use MintyPHP\Module\Helpdesk\Repository\HandoverRevisionRepository;
use MintyPHP\Module\Helpdesk\Service\HandoverRevisionService;
use MintyPHP\Module\Helpdesk\Service\HandoverService;
use PHPUnit\Framework\TestCase;
class HandoverRevisionServiceTest extends TestCase
{
private const TENANT_ID = 1;
private const USER_ID = 42;
private const HANDOVER_ID = 10;
private function createService(
?HandoverRevisionRepository $revisionRepo = null,
?HandoverRepository $handoverRepo = null,
): HandoverRevisionService {
$revisionRepo = $revisionRepo ?? $this->createMock(HandoverRevisionRepository::class);
$handoverRepo = $handoverRepo ?? $this->createMock(HandoverRepository::class);
return new HandoverRevisionService($revisionRepo, $handoverRepo);
}
// ── createRevision ───────────────────────────────────────────────
public function testCreateRevisionIncrementsNumber(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('getLatestRevisionNumber')->willReturn(3);
$revisionRepo->expects($this->once())
->method('insert')
->with($this->callback(function (array $data): bool {
return $data['revision'] === 4;
}))
->willReturn(100);
$service = $this->createService($revisionRepo);
$result = $service->createRevision(self::TENANT_ID, self::HANDOVER_ID, ['name' => 'Test'], 'draft', 1, self::USER_ID);
$this->assertSame(100, $result);
}
public function testCreateRevisionStoresSnapshot(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('getLatestRevisionNumber')->willReturn(0);
$revisionRepo->expects($this->once())
->method('insert')
->with($this->callback(function (array $data): bool {
return $data['status'] === 'in_progress'
&& $data['schema_version'] === 2
&& $data['change_type'] === 'fields'
&& json_decode($data['field_values'], true) === ['name' => 'Acme'];
}))
->willReturn(1);
$service = $this->createService($revisionRepo);
$service->createRevision(self::TENANT_ID, self::HANDOVER_ID, ['name' => 'Acme'], 'in_progress', 2, self::USER_ID);
}
// ── computeDiff ──────────────────────────────────────────────────
public function testComputeDiffDetectsAddedFields(): void
{
$service = $this->createService();
$diff = $service->computeDiff([], ['name' => 'New']);
$this->assertArrayHasKey('name', $diff);
$this->assertSame('added', $diff['name']['type']);
$this->assertNull($diff['name']['old']);
$this->assertSame('New', $diff['name']['new']);
}
public function testComputeDiffDetectsRemovedFields(): void
{
$service = $this->createService();
$diff = $service->computeDiff(['name' => 'Old'], []);
$this->assertArrayHasKey('name', $diff);
$this->assertSame('removed', $diff['name']['type']);
$this->assertSame('Old', $diff['name']['old']);
$this->assertNull($diff['name']['new']);
}
public function testComputeDiffDetectsChangedFields(): void
{
$service = $this->createService();
$diff = $service->computeDiff(['name' => 'Old', 'priority' => 'low'], ['name' => 'New', 'priority' => 'low']);
$this->assertArrayHasKey('name', $diff);
$this->assertSame('changed', $diff['name']['type']);
$this->assertSame('Old', $diff['name']['old']);
$this->assertSame('New', $diff['name']['new']);
$this->assertArrayNotHasKey('priority', $diff);
}
public function testComputeDiffHandlesEmptyArrays(): void
{
$service = $this->createService();
$diff = $service->computeDiff([], []);
$this->assertSame([], $diff);
}
// ── restoreRevision ──────────────────────────────────────────────
public function testRestoreRevisionRequiresManagePermission(): void
{
$service = $this->createService();
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 1, self::USER_ID, HandoverService::PERMISSION_CREATE);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('general', $result['errors']);
}
public function testRestoreRevisionRejectsInvalidRevision(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('findByRevision')->willReturn(null);
$service = $this->createService($revisionRepo);
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 999, self::USER_ID, HandoverService::PERMISSION_MANAGE);
$this->assertFalse($result['ok']);
}
public function testRestoreRevisionCreatesNewRevision(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('findByRevision')->willReturn([
'id' => 1,
'revision' => 2,
'field_values' => '{"name":"Old value"}',
'status' => 'draft',
]);
$revisionRepo->method('getLatestRevisionNumber')->willReturn(5);
$revisionRepo->expects($this->once())
->method('insert')
->with($this->callback(function (array $data): bool {
return $data['revision'] === 6
&& $data['change_type'] === 'restore'
&& json_decode($data['field_values'], true) === ['name' => 'Old value'];
}))
->willReturn(200);
$handoverRepo = $this->createMock(HandoverRepository::class);
$handoverRepo->method('findById')->willReturn([
'id' => self::HANDOVER_ID,
'status' => 'in_progress',
'field_values' => '{"name":"Current value"}',
'schema_snapshot' => '{"version":1,"fields":[]}',
]);
$handoverRepo->expects($this->once())->method('beginTransaction');
$handoverRepo->expects($this->once())->method('commitTransaction');
$handoverRepo->expects($this->never())->method('rollbackTransaction');
$handoverRepo->expects($this->once())->method('updateFieldValues')->willReturn(true);
$handoverRepo->expects($this->once())->method('updateStatus')->willReturn(true);
$service = $this->createService($revisionRepo, $handoverRepo);
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 2, self::USER_ID, HandoverService::PERMISSION_MANAGE);
$this->assertTrue($result['ok']);
}
public function testRestoreRevisionSkipsStatusUpdateWhenSame(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('findByRevision')->willReturn([
'id' => 1,
'revision' => 1,
'field_values' => '{"name":"Old"}',
'status' => 'draft',
]);
$revisionRepo->method('getLatestRevisionNumber')->willReturn(3);
$revisionRepo->method('insert')->willReturn(100);
$handoverRepo = $this->createMock(HandoverRepository::class);
$handoverRepo->method('findById')->willReturn([
'id' => self::HANDOVER_ID,
'status' => 'draft',
'field_values' => '{"name":"Current"}',
'schema_snapshot' => '{"version":1,"fields":[]}',
]);
$handoverRepo->expects($this->once())->method('beginTransaction');
$handoverRepo->expects($this->once())->method('commitTransaction');
$handoverRepo->expects($this->never())->method('rollbackTransaction');
$handoverRepo->expects($this->once())->method('updateFieldValues')->willReturn(true);
$handoverRepo->expects($this->never())->method('updateStatus');
$service = $this->createService($revisionRepo, $handoverRepo);
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 1, self::USER_ID, HandoverService::PERMISSION_MANAGE);
$this->assertTrue($result['ok']);
}
public function testRestoreRevisionRollsBackWhenFieldUpdateFails(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('findByRevision')->willReturn([
'id' => 1,
'revision' => 1,
'field_values' => '{"name":"Old"}',
'status' => 'draft',
]);
$revisionRepo->expects($this->never())->method('insert');
$handoverRepo = $this->createMock(HandoverRepository::class);
$handoverRepo->method('findById')->willReturn([
'id' => self::HANDOVER_ID,
'status' => 'draft',
'field_values' => '{"name":"Current"}',
'schema_snapshot' => '{"version":1,"fields":[]}',
]);
$handoverRepo->expects($this->once())->method('beginTransaction');
$handoverRepo->expects($this->once())->method('rollbackTransaction');
$handoverRepo->expects($this->never())->method('commitTransaction');
$handoverRepo->expects($this->once())->method('updateFieldValues')->willReturn(false);
$handoverRepo->expects($this->never())->method('updateStatus');
$service = $this->createService($revisionRepo, $handoverRepo);
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 1, self::USER_ID, HandoverService::PERMISSION_MANAGE);
$this->assertFalse($result['ok']);
$this->assertArrayHasKey('general', $result['errors']);
}
public function testRestoreRevisionRollsBackWhenRevisionInsertFails(): void
{
$revisionRepo = $this->createMock(HandoverRevisionRepository::class);
$revisionRepo->method('findByRevision')->willReturn([
'id' => 1,
'revision' => 1,
'field_values' => '{"name":"Old"}',
'status' => 'draft',
]);
$revisionRepo->method('getLatestRevisionNumber')->willReturn(3);
$revisionRepo->expects($this->once())->method('insert')->willReturn(null);
$handoverRepo = $this->createMock(HandoverRepository::class);
$handoverRepo->method('findById')->willReturn([
'id' => self::HANDOVER_ID,
'status' => 'draft',
'field_values' => '{"name":"Current"}',
'schema_snapshot' => '{"version":1,"fields":[]}',
]);
$handoverRepo->expects($this->once())->method('beginTransaction');
$handoverRepo->expects($this->once())->method('rollbackTransaction');
$handoverRepo->expects($this->never())->method('commitTransaction');
$handoverRepo->expects($this->once())->method('updateFieldValues')->willReturn(true);
$handoverRepo->expects($this->never())->method('updateStatus');
$service = $this->createService($revisionRepo, $handoverRepo);
$result = $service->restoreRevision(self::TENANT_ID, self::HANDOVER_ID, 1, self::USER_ID, HandoverService::PERMISSION_MANAGE);
$this->assertFalse($result['ok']);
}
}